Skip to content

feat: excludedUsersをcompanyGithubUsers.typeに統合#215

Merged
coji merged 11 commits intomainfrom
feat/migrate-excluded-users-to-bot-type
Mar 19, 2026
Merged

feat: excludedUsersをcompanyGithubUsers.typeに統合#215
coji merged 11 commits intomainfrom
feat/migrate-excluded-users-to-bot-type

Conversation

@coji
Copy link
Owner

@coji coji commented Mar 18, 2026

Summary

  • organizationSettings.excludedUsers(カンマ区切りテキスト)を廃止し、companyGithubUsers.type='Bot'に統合
  • batch処理のfilterActors()がDBのBot typeを直接参照するように変更
  • GitHub APIの__typename === 'Bot'情報を使って新規ユーザーを自動でBot設定
  • 設定画面のExcluded Usersフィールドを削除(Bot管理はGitHub Users設定画面のtypeで行う)
  • GitHub Users一覧にlastActivityAtカラムを追加し、最終活動日の降順でデフォルトソート

変更詳細

  • マイグレーション: 既存excludedUsers値をcompanyGithubUsers.type='Bot'に移行(lowercase)、excluded_usersカラムDROP
  • マイグレーション: last_activity_atカラム追加、既存PR/reviewデータからbackfill
  • batch: DEFAULT_EXCLUDED_USERS定数とハードコードリストを削除、GitHub API isBotフラグで自動判定
  • batch: BuildConfig.excludedUsersbotLogins: Set<string>(case-insensitive)
  • batch: upsertAnalyzedDataでユーザーごとの最終活動日を自動更新
  • ジョブ: getBotLoginsgetOrganizationで並行取得(listAllOrganizationsは対象外)
  • UI: 設定画面からexcludedUsersフィールド・スキーマ・ミューテーションを削除
  • UI: GitHub Users一覧のデフォルトソートをlastActivityAt descに変更、fromNow()で相対表示

Closes #146

Test plan

  • pnpm validate パス(lint, format, typecheck, build, test)
  • pnpm db:setup パス(seed DBでマイグレーション適用確認)
  • GitHub Users設定画面でtypeをBotに変更 → recalculate実行でサイクルタイムに反映されることを確認
  • 新規orgでcrawl実行 → isBotなユーザーがtype='Bot'で自動登録されることを確認
  • GitHub Users一覧がlastActivityAt降順でソートされ、NULLが末尾に表示されることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

リリースノート

  • New Features

    • GitHubユーザーの最終アクティビティ時刻を追跡・表示するようになりました。
    • プルリクエストの作成者がボットであるかどうかを識別するようになりました。
  • Bug Fixes

    • 組織設定の「Excluded Users」フィールドを削除し、ボットログイン管理に置き換えました。
    • GitHub ユーザーテーブルのデフォルトソート順序を最新アクティビティ降順に変更しました。

excludedUsersをorganizationSettingsのカンマ区切りテキストから
companyGithubUsers.type='Bot'に統合。batch処理のfilterActors()が
DBのBot typeを直接参照するようになり、設定UIのexcludedUsersフィールドを削除。

- マイグレーションで既存excludedUsers値をcompanyGithubUsers.type='Bot'に移行
- DEFAULT_EXCLUDED_USERSハードコードを削除し、DBベースのBot管理に統一
- well-known bot(copilot, dependabot, renovate等)を新規登録時に自動Bot設定
- BuildConfig.excludedUsers → botLogins: Set<string>に変更(case-insensitive)
- getBotLoginsをgetTenantDataに統合し並行クエリ化

Closes #146

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Warning

Rate limit exceeded

@coji has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1af59a0-9f67-4d60-b43d-48b4ec582934

📥 Commits

Reviewing files that changed from the base of the PR and between d2cac0e and cd4c18b.

⛔ Files ignored due to path filters (1)
  • db/migrations/tenant/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • app/routes/$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts
  • batch/db/mutations.ts
  • db/migrations/tenant/20260319120000_add_last_activity_at.sql
📝 Walkthrough

Walkthrough

organizationSettings の excludedUsers を削除し、代替として company_github_users の Bot タイプ(botLogins)へ移行。DBマイグレーション、UI/スキーマ/ミューテーション、バッチ・ワーカー入出力と呼び出しを botLogins ベースへ置換した。

Changes

Cohort / File(s) Summary
組織設定 UI / スキーマ / ミューテーション
app/routes/$orgSlug/settings/_index/+forms/organization-settings.tsx, app/routes/$orgSlug/settings/_index/+schema.ts, app/routes/$orgSlug/settings/_index/+functions/mutations.server.ts, app/routes/$orgSlug/settings/_index/index.tsx
フォーム/スキーマ/ミューテーション/送信処理から excludedUsers を完全削除。デフォルト値と入力フィールドが除去された。
型定義・ワーカー入力/出力
app/services/tenant-type.ts, app/services/jobs/run-in-worker.ts, app/services/jobs/analyze-worker.ts
型から excludedUsers を削除し、ワーカー入力を botLogins: string[] に変更。ワーカー出力に botUsers を追加して postMessage で配列を返すよう変更。
バッチ DB クエリ・マイグレーション
batch/db/queries.ts, batch/db/mutations.ts, db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql, db/tenant.sql
excluded_users を company_github_users の type='Bot' に移行する SQL とマイグレーションを追加。getBotLogins を導入し、upsert 系に botUsers/botUsers?: Set を伝搬。DB スキーマから excluded_users を削除、last_activity_at を追加。
バッチ PR 処理(フィルタ / 収集)
batch/github/pullrequest.ts, batch/github/__tests__/buildPullRequests-filter.test.ts, batch/github/fetcher.ts, batch/github/model.ts, batch/github/__tests__/release-detect.test.ts, batch/github/store.test.ts
DEFAULT_EXCLUDED_USERS と excludedUsers ロジックを廃止。BuildConfig が botLogins: Set<string> を受け、PR 形状に authorIsBot を追加。buildPullRequests が botUsers を収集して返すよう拡張。テストフィクスチャを更新。
ジョブパイプライン / 共有ステップ
app/services/jobs/shared-steps.server.ts, app/services/jobs/crawl.server.ts, app/services/jobs/recalculate.server.ts, app/services/jobs/analyze-worker.ts
OrganizationData に botLogins を追加し、ワーカー呼び出しへ botLogins を渡す。分析結果を集約して botUsers を upsert ペイロードへ反映。
リポジトリ/PR UI 統合
app/routes/$orgSlug/settings/repositories/$repository/$pull/index.tsx, app/routes/$orgSlug/settings/repositories/$repository/$pull/queries.server.ts
getBotLogins を追加して並列フェッチ。organizationSettings の projection から excludedUsers を削除し、buildPullRequests に botLogins を渡すよう変更。
GitHub Users 表示 / ソート
app/routes/$orgSlug/settings/github-users._index/+components/github-users-columns.tsx, app/routes/$orgSlug/settings/github-users._index/queries.server.ts, app/routes/$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts
company_github_users に lastActivityAt を追加取得し、表示カラムを Last Activity に変更。NULL-対応のソートを導入。デフォルトソート順を desc に変更。

Sequence Diagram(s)

sequenceDiagram
    participant UI as 組織設定UI
    participant App as アプリサーバ
    participant TenantDB as Tenant DB\n(organization_settings)
    participant Batch as バッチプロセス
    participant BatchDB as Batch DB\n(company_github_users)
    participant Worker as 分析ワーカー

    Note over UI,App: excludedUsers フィールドは UI から削除
    UI->>App: 設定保存 (excludedUsers を送らない)
    App->>TenantDB: organization_settings 更新(excluded_users 列なし)

    Batch->>BatchDB: getBotLogins(organizationId)
    BatchDB-->>Batch: botLogins (string[]/Set)
    Batch->>Worker: Analyze 実行 (botLogins を含む)
    Worker-->>Batch: result + botUsers
    Batch->>BatchDB: upsertCompanyGithubUsers(..., botUsers)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 ぼくはラビット、コードの畑でぴょん
excludedUsers をそっと土へ返してぴょん
Bot は名簿に並び、botLogins で踊るよ🌱
バッチは呼び出し、ワーカーが歌う、結果はぴょん!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは PR の主要な変更を正確に要約しており、excludedUsers から companyGithubUsers.type への統合という中核的な内容を明確に反映しています。
Linked Issues check ✅ Passed PR は #146 で指定されたすべての主要な実装要件を満たしており、excludedUsers の削除、botLogins への置換、DB マイグレーション、UI 削除、テスト更新が完了しています。
Out of Scope Changes check ✅ Passed organization_settings、batch、DB マイグレーション、UI の変更はすべて #146 の統合目的に直結しており、PR 範囲内の一貫性を保っています。last_activity_at の追加も関連目標です。

✏️ 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 feat/migrate-excluded-users-to-bot-type
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Copy link

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
batch/db/mutations.ts (1)

251-263: ⚠️ Potential issue | 🟠 Major

既存の既知Bot が type='Bot' に昇格しない経路が残っています。

doNothing() のため、既に存在する login は Line 257 の type 付与が適用されません。
既知Bot が例えば app/routes の mutation 経由で type=null で作成されると、その後 batch function を実行しても type 更新されません。migration は一度きりのため、継続的なデータ整合性を保証しません。

💡 修正案(既存行は type IS NULL の場合のみ昇格)
   await tenantDb
     .insertInto('companyGithubUsers')
     .values(
       uniqueLogins.map((login) => ({
         login,
         displayName: login,
         type: KNOWN_BOTS.has(login) ? 'Bot' : null,
         isActive: 0,
         updatedAt: now,
       })),
     )
     .onConflict((oc) => oc.column('login').doNothing())
     .execute()
+
+  const knownBotLogins = uniqueLogins.filter((login) => KNOWN_BOTS.has(login))
+  if (knownBotLogins.length > 0) {
+    await tenantDb
+      .updateTable('companyGithubUsers')
+      .set({ type: 'Bot', updatedAt: now })
+      .where('login', 'in', knownBotLogins)
+      .where('type', 'is', null)
+      .execute()
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@batch/db/mutations.ts` around lines 251 - 263, The insert currently uses
onConflict(...).doNothing(), so existing rows never get their type updated to
'Bot'; change the upsert to update existing rows only when their type IS NULL.
Replace the onConflict(...).doNothing() call on
tenantDb.insertInto('companyGithubUsers') with an onConflict handler that does
an update (e.g., onConflict((oc) =>
oc.column('login').doUpdateSet(...).where('type', 'is', null'))) and set type:
'Bot' (and update updatedAt) for rows where companyGithubUsers.type IS NULL,
using the same KNOWN_BOTS lookup for the values generated from uniqueLogins.
🧹 Nitpick comments (1)
batch/github/__tests__/buildPullRequests-filter.test.ts (1)

204-210: botLogins の大小文字差異を検知する回帰テストを1本追加しておくと安全です。

今回の移行ポイントが「ケースインセンシティブ」なので、new Set(['Dependabot']) でも dependabot が除外されることを固定化しておくのをおすすめします。

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

In `@batch/github/__tests__/buildPullRequests-filter.test.ts` around lines 204 -
210, Add a regression test to assert case-insensitive bot login matching: update
the test suite in buildPullRequests-filter.test.ts to include a config where
botLogins is populated with a differently-cased entry (e.g., new
Set(['Dependabot'])), then run the existing filter logic (the code paths that
read config.botLogins) and assert that a pull request authored by 'dependabot'
is excluded; ensure you reference the existing config object and botLogins
symbol and name the test to indicate case-insensitivity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@batch/db/queries.ts`:
- Around line 69-70: botLogins is returned without lower-case normalization
causing downstream matches (which call user.toLowerCase()) to miss entries like
"Dependabot"; update the mapping in getTenantData (the botLogins:
botLoginRows.map(...) expression) to normalize with .toLowerCase(), and also
ensure getBotLogins returns lower-cased logins too so both entry points
consistently yield lower-case bot login strings.

In `@db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql`:
- Around line 3-12: The migration currently builds a JSON array from
organization_settings.excluded_users and feeds it to json_each which will break
if excluded_users contains quotes, backslashes or newlines; instead, replace the
json_each approach with a safe comma-splitting function (e.g.
regexp_split_to_table(excluded_users, '\s*,\s*') or
unnest(string_to_array(excluded_users, ','))) and then TRIM/value-filter empty
strings, LOWER/TRIM each value and use that result to INSERT into
company_github_users (referencing organization_settings, excluded_users,
company_github_users and the ON CONFLICT clause) so you do not construct JSON at
all.
- Around line 14-17: The migration only backfills 'copilot' as a Bot but misses
other well-known bots listed in KNOWN_BOTS (e.g., dependabot, renovate,
github-actions), so update the INSERT/ON CONFLICT in the migration (file with
INSERT INTO company_github_users ...) to include those logins too (insert rows
for 'dependabot', 'renovate', 'github-actions', etc., all lowercase and with
type = 'Bot' and is_active = 0), ensuring the ON CONFLICT DO UPDATE still sets
type = 'Bot' so existing rows are converted; reference KNOWN_BOTS in
batch/db/mutations.ts to ensure parity of the list.

---

Outside diff comments:
In `@batch/db/mutations.ts`:
- Around line 251-263: The insert currently uses onConflict(...).doNothing(), so
existing rows never get their type updated to 'Bot'; change the upsert to update
existing rows only when their type IS NULL. Replace the
onConflict(...).doNothing() call on tenantDb.insertInto('companyGithubUsers')
with an onConflict handler that does an update (e.g., onConflict((oc) =>
oc.column('login').doUpdateSet(...).where('type', 'is', null'))) and set type:
'Bot' (and update updatedAt) for rows where companyGithubUsers.type IS NULL,
using the same KNOWN_BOTS lookup for the values generated from uniqueLogins.

---

Nitpick comments:
In `@batch/github/__tests__/buildPullRequests-filter.test.ts`:
- Around line 204-210: Add a regression test to assert case-insensitive bot
login matching: update the test suite in buildPullRequests-filter.test.ts to
include a config where botLogins is populated with a differently-cased entry
(e.g., new Set(['Dependabot'])), then run the existing filter logic (the code
paths that read config.botLogins) and assert that a pull request authored by
'dependabot' is excluded; ensure you reference the existing config object and
botLogins symbol and name the test to indicate case-insensitivity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e29fc7e7-d0c3-433d-b51f-fc4b333a1239

📥 Commits

Reviewing files that changed from the base of the PR and between 31a308a and 836dcee.

⛔ Files ignored due to path filters (1)
  • db/migrations/tenant/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • app/routes/$orgSlug/settings/_index/+forms/organization-settings.tsx
  • app/routes/$orgSlug/settings/_index/+functions/mutations.server.ts
  • app/routes/$orgSlug/settings/_index/+schema.ts
  • app/routes/$orgSlug/settings/_index/index.tsx
  • app/routes/$orgSlug/settings/repositories/$repository/$pull/index.tsx
  • app/routes/$orgSlug/settings/repositories/$repository/$pull/queries.server.ts
  • app/services/jobs/analyze-worker.ts
  • app/services/jobs/crawl.server.ts
  • app/services/jobs/recalculate.server.ts
  • app/services/jobs/run-in-worker.ts
  • app/services/jobs/shared-steps.server.ts
  • app/services/tenant-type.ts
  • batch/db/mutations.ts
  • batch/db/queries.ts
  • batch/github/__tests__/buildPullRequests-filter.test.ts
  • batch/github/pullrequest.ts
  • db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql
  • db/tenant.sql
💤 Files with no reviewable changes (6)
  • db/tenant.sql
  • app/routes/$orgSlug/settings/_index/+schema.ts
  • app/routes/$orgSlug/settings/_index/index.tsx
  • app/routes/$orgSlug/settings/_index/+functions/mutations.server.ts
  • app/routes/$orgSlug/settings/_index/+forms/organization-settings.tsx
  • app/services/tenant-type.ts

Comment on lines 69 to 70
botLogins: botLoginRows.map((r) => r.login),
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

botLogins を小文字正規化しないと Bot フィルタが漏れます。

下流の照合は user.toLowerCase() 前提です。ここで生値を返すと、DBに Dependabot などが入っていた場合に一致しません。
getTenantData / getBotLogins の両方で lower-case 正規化してください。

💡 修正案
   return {
     organizationSetting: organizationSetting ?? null,
     integration: integration ?? null,
     repositories,
     exportSetting: exportSetting ?? null,
-    botLogins: botLoginRows.map((r) => r.login),
+    botLogins: botLoginRows.map((r) => r.login.toLowerCase()),
   }
 }
@@
 export async function getBotLogins(
   organizationId: OrganizationId,
 ): Promise<Set<string>> {
@@
-  return new Set(rows.map((r) => r.login))
+  return new Set(rows.map((r) => r.login.toLowerCase()))
 }

Also applies to: 73-83

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

In `@batch/db/queries.ts` around lines 69 - 70, botLogins is returned without
lower-case normalization causing downstream matches (which call
user.toLowerCase()) to miss entries like "Dependabot"; update the mapping in
getTenantData (the botLogins: botLoginRows.map(...) expression) to normalize
with .toLowerCase(), and also ensure getBotLogins returns lower-cased logins too
so both entry points consistently yield lower-case bot login strings.

Comment on lines +3 to +12
INSERT INTO company_github_users (login, display_name, type, is_active, updated_at)
SELECT
LOWER(TRIM(value)) AS login,
LOWER(TRIM(value)) AS display_name,
'Bot' AS type,
0 AS is_active,
CURRENT_TIMESTAMP AS updated_at
FROM organization_settings, json_each('["' || REPLACE(excluded_users, ',', '","') || '"]')
WHERE TRIM(value) != ''
ON CONFLICT (login) DO UPDATE SET type = 'Bot';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

excluded_users を JSON 文字列に組み立てる前提が危ないです。

元列は制約のない text なので、過去データに " / \ / 改行が 1 件でも入っていると json_each(...) が壊れて migration 全体が止まる可能性があります。ここは JSON を合成せず、カンマ分割をそのまま処理する形にしたほうが安全です。

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

In `@db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql`
around lines 3 - 12, The migration currently builds a JSON array from
organization_settings.excluded_users and feeds it to json_each which will break
if excluded_users contains quotes, backslashes or newlines; instead, replace the
json_each approach with a safe comma-splitting function (e.g.
regexp_split_to_table(excluded_users, '\s*,\s*') or
unnest(string_to_array(excluded_users, ','))) and then TRIM/value-filter empty
strings, LOWER/TRIM each value and use that result to INSERT into
company_github_users (referencing organization_settings, excluded_users,
company_github_users and the ON CONFLICT clause) so you do not construct JSON at
all.

Comment on lines +14 to +17
-- 2. Ensure 'copilot' is registered as Bot (case-insensitive - stored lowercase)
INSERT INTO company_github_users (login, display_name, type, is_active, updated_at)
VALUES ('copilot', 'copilot', 'Bot', 0, CURRENT_TIMESTAMP)
ON CONFLICT (login) DO UPDATE SET type = 'Bot';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

copilot だけでは既存 bot の backfill が足りません。

この PR では以後 company_github_users.type = 'Bot' だけを見に行くので、excluded_users に入っていない既存の well-known bot は Bot に寄せられず、tenant によっては人間扱いのまま残ります。少なくとも batch/db/mutations.tsKNOWN_BOTS にある dependabot / renovate / github-actions などもここで backfill しておきたいです。

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

In `@db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql`
around lines 14 - 17, The migration only backfills 'copilot' as a Bot but misses
other well-known bots listed in KNOWN_BOTS (e.g., dependabot, renovate,
github-actions), so update the INSERT/ON CONFLICT in the migration (file with
INSERT INTO company_github_users ...) to include those logins too (insert rows
for 'dependabot', 'renovate', 'github-actions', etc., all lowercase and with
type = 'Bot' and is_active = 0), ensuring the ON CONFLICT DO UPDATE still sets
type = 'Bot' so existing rows are converted; reference KNOWN_BOTS in
batch/db/mutations.ts to ensure parity of the list.

coji and others added 2 commits March 19, 2026 00:15
- Remove 'claude' and 'chatgpt-codex-connector' from KNOWN_BOTS (human name collision risk)
- Add COALESCE and empty-string guard to migration SQL for robustness

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
KNOWN_BOTSハードコードリストを削除し、GitHub APIの__typename === 'Bot'
情報をbuildPullRequestsからupsertCompanyGithubUsersに流すように変更。
新規ユーザー登録時にGitHub APIが返すbot判定を自動的にtype='Bot'として保存。

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
batch/db/mutations.ts (1)

252-260: ⚠️ Potential issue | 🔴 Critical

マイグレーションで KNOWN_BOTS のすべてのボットが Bot として設定されていない

KNOWN_BOTS には 8 つのボット(copilot, copilot-pull-request-reviewer, copilot-swe-agent, github-actions, dependabot, renovate, coderabbitai, devin-ai-integration)が定義されていますが、マイグレーションファイル(db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql)では以下の問題があります:

  • 明示的に Bot として設定されているのは copilot のみ
  • 第 1 部では organization_settings.excluded_users から移行されるボットのみが処理されており、他の 6 つのボット(copilot-pull-request-reviewer, copilot-swe-agent, github-actions, renovate, coderabbitai, devin-ai-integration)はexcluded_usersに含まれていなければtype='Bot'に設定されません
  • mutations.tsdoNothing() により、既存レコードは上書きされません

すべての KNOWN_BOTS をマイグレーションで type='Bot' に設定してください。

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

In `@batch/db/mutations.ts` around lines 252 - 260, The insert currently builds
rows from uniqueLogins and uses onConflict(...).doNothing(), so existing rows
aren’t updated and only logins present in the excluded_users migration path get
type='Bot'; update the logic to ensure every login in KNOWN_BOTS is
persisted/updated with type='Bot' by (a) including all KNOWN_BOTS in the set of
inserted/processed logins and (b) replacing onConflict(...).doNothing() with an
upsert that merges/updates the type field (e.g., onConflict(...).merge() or an
UPDATE setting type='Bot') so existing records for KNOWN_BOTS get type='Bot';
adjust the migration SQL (the migrate_excluded_users_to_bot_type migration
logic) similarly so all entries in KNOWN_BOTS are set to type='Bot' regardless
of excluded_users membership.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@batch/db/mutations.ts`:
- Around line 252-260: The insert currently builds rows from uniqueLogins and
uses onConflict(...).doNothing(), so existing rows aren’t updated and only
logins present in the excluded_users migration path get type='Bot'; update the
logic to ensure every login in KNOWN_BOTS is persisted/updated with type='Bot'
by (a) including all KNOWN_BOTS in the set of inserted/processed logins and (b)
replacing onConflict(...).doNothing() with an upsert that merges/updates the
type field (e.g., onConflict(...).merge() or an UPDATE setting type='Bot') so
existing records for KNOWN_BOTS get type='Bot'; adjust the migration SQL (the
migrate_excluded_users_to_bot_type migration logic) similarly so all entries in
KNOWN_BOTS are set to type='Bot' regardless of excluded_users membership.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d0e38d3e-dc0f-41a3-a86c-507b068a711b

📥 Commits

Reviewing files that changed from the base of the PR and between 836dcee and 3059344.

⛔ Files ignored due to path filters (1)
  • db/migrations/tenant/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • batch/db/mutations.ts
  • db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql
🚧 Files skipped from review as they are similar to previous changes (1)
  • db/migrations/tenant/20260318120000_migrate_excluded_users_to_bot_type.sql

…lOrganizations

- Fix duplicate step 3 comment in buildPullRequests (renumber 3-8 → 3-9)
- Make getBotLogins the single source for bot login queries (remove from getTenantData)
- getOrganization fetches botLogins in parallel with tenant data
- listAllOrganizations no longer fetches unnecessary bot logins

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link

@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 (1)
batch/db/mutations.ts (1)

241-250: 既存ログイン衝突時に Bot 昇格できないため、判定の取りこぼしが残ります

Line 249 の doNothing() により、既存レコードが type = null の場合でも Line 244 の Bot 判定結果を反映できません。type 未設定時のみ Bot に更新する条件付き upsert を検討してください。

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

In `@batch/db/mutations.ts` around lines 241 - 250, The current insert uses
onConflict(...).doNothing() so existing rows with type=null never get promoted
to 'Bot' even when botUsers contains the login; replace the doNothing upsert
with a conditional upsert that updates type to 'Bot' only when the existing
row's type is null: change the onConflict handler (the builder that currently
calls .doNothing()) to a doUpdate that sets the type column to 'Bot' when type
IS NULL (or uses COALESCE/CASE comparing existing type vs excluded value),
ensuring the uniqueLogins.map and botUsers check remain the source of truth for
whether to attempt Bot promotion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@batch/github/pullrequest.ts`:
- Around line 227-233: The current loop only adds bot users from
rawArtifacts.reviews and rawArtifacts.discussions, missing cases where the PR
author is a bot; update the collection logic to also inspect rawArtifacts.author
(or the PR author object) and add it to botUsers when it indicates a bot (e.g.,
author.isBot or author.__typename === 'Bot' and author.username exists),
ensuring you call toLowerCase() like the others and handle a missing author
safely; modify the code around rawArtifacts.reviews/rawArtifacts.discussions and
botUsers to include this author check.

---

Nitpick comments:
In `@batch/db/mutations.ts`:
- Around line 241-250: The current insert uses onConflict(...).doNothing() so
existing rows with type=null never get promoted to 'Bot' even when botUsers
contains the login; replace the doNothing upsert with a conditional upsert that
updates type to 'Bot' only when the existing row's type is null: change the
onConflict handler (the builder that currently calls .doNothing()) to a doUpdate
that sets the type column to 'Bot' when type IS NULL (or uses COALESCE/CASE
comparing existing type vs excluded value), ensuring the uniqueLogins.map and
botUsers check remain the source of truth for whether to attempt Bot promotion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ff08635-988a-40c9-807d-0a849551d27c

📥 Commits

Reviewing files that changed from the base of the PR and between 3059344 and e68c754.

📒 Files selected for processing (4)
  • app/services/jobs/analyze-worker.ts
  • app/services/jobs/shared-steps.server.ts
  • batch/db/mutations.ts
  • batch/github/pullrequest.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/services/jobs/analyze-worker.ts

coji and others added 5 commits March 19, 2026 08:49
- Add last_activity_at column to company_github_users with backfill migration
- Update batch upsert to track latest PR/review activity per user
- Default sort GitHub Users by last activity (desc), NULL last
- Display last activity as relative time (fromNow)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
… helper

- NULL-last ordering only applies when sorting by lastActivityAt column
- Extract trackLatest() helper to deduplicate Map.get/set pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- getBotLogins returns lowercase-normalized logins for consistent matching
- Add authorIsBot to ShapedGitHubPullRequest via GraphQL __typename
- Collect PR author bots in buildPullRequests (e.g. dependabot PRs)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Change onConflict from doNothing to doUpdateSet with COALESCE — if an
existing user has type=null and the API detects them as Bot, their type
is now updated. Manually set types (User/Bot by admin) are preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link

@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 (2)
app/routes/$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts (1)

30-30: updateSort のフォールバック値との不整合

デフォルトのソート順が 'desc' に変更されましたが、updateSort 関数(Line 102)では newSort.sort_order || 'asc' としてフォールバックが 'asc' のままです。これによりユーザーがカラムヘッダーをクリックした際に、デフォルトの期待値と異なる動作になる可能性があります。

🔧 修正案
  const updateSort = (newSort: Partial<Sort>) => {
    setSearchParams(
      (prev) => {
        if (newSort.sort_by) {
          prev.set('sort_by', newSort.sort_by)
-          prev.set('sort_order', newSort.sort_order || 'asc')
+          prev.set('sort_order', newSort.sort_order || 'desc')
        } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/routes/`$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts
at line 30, The default sort order was changed to 'desc' but updateSort still
falls back to 'asc', causing inconsistent behavior; update the fallback in the
updateSort function (referenced as updateSort) to use 'desc' (i.e., use
newSort.sort_order || 'desc') so clicks obey the declared default('desc')
sorting behavior and keep sort handling consistent with the schema/default.
batch/db/mutations.ts (1)

257-262: trackLatest の条件式を簡潔にできます

map.has(key) のチェック後は map.get(key) が必ず値を返すため、?? '' は不要です。

♻️ 提案する修正
 function trackLatest(map: Map<string, string>, login: string, ts: string) {
   const key = login.toLowerCase()
-  if (!map.has(key) || ts > (map.get(key) ?? '')) {
+  const existing = map.get(key)
+  if (!existing || ts > existing) {
     map.set(key, ts)
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@batch/db/mutations.ts` around lines 257 - 262, The conditional in trackLatest
currently uses "ts > (map.get(key) ?? '')" which is redundant because after
checking map.has(key) the map.get(key) will always be defined; simplify the
condition by removing the nullish coalescing and compare ts directly to
map.get(key) (e.g., ts > map.get(key)) so the function reads clearly and avoids
an unnecessary fallback. Ensure you only change the expression inside
trackLatest and keep the key normalization and map.set behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@db/migrations/tenant/20260319120000_add_last_activity_at.sql`:
- Around line 5-13: The backfill compares LOWER(author) and LOWER(reviewer) to
company_github_users.login without normalizing the login side, so mixed-case
logins (e.g., "Dependabot") won't match; update the query to normalize both
sides (e.g., compare LOWER(author) = LOWER(company_github_users.login) and
LOWER(reviewer) = LOWER(company_github_users.login)) when computing
last_activity_at for company_github_users using the pull_requests and
pull_request_reviews subqueries so all case variants match correctly.

---

Nitpick comments:
In
`@app/routes/`$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts:
- Line 30: The default sort order was changed to 'desc' but updateSort still
falls back to 'asc', causing inconsistent behavior; update the fallback in the
updateSort function (referenced as updateSort) to use 'desc' (i.e., use
newSort.sort_order || 'desc') so clicks obey the declared default('desc')
sorting behavior and keep sort handling consistent with the schema/default.

In `@batch/db/mutations.ts`:
- Around line 257-262: The conditional in trackLatest currently uses "ts >
(map.get(key) ?? '')" which is redundant because after checking map.has(key) the
map.get(key) will always be defined; simplify the condition by removing the
nullish coalescing and compare ts directly to map.get(key) (e.g., ts >
map.get(key)) so the function reads clearly and avoids an unnecessary fallback.
Ensure you only change the expression inside trackLatest and keep the key
normalization and map.set behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4292d5f-692a-488a-b4f2-2a249533b6b0

📥 Commits

Reviewing files that changed from the base of the PR and between e68c754 and d2cac0e.

⛔ Files ignored due to path filters (1)
  • db/migrations/tenant/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • app/routes/$orgSlug/settings/github-users._index/+components/github-users-columns.tsx
  • app/routes/$orgSlug/settings/github-users._index/+hooks/use-data-table-state.ts
  • app/routes/$orgSlug/settings/github-users._index/queries.server.ts
  • app/routes/$orgSlug/settings/repositories/$repository/$pull/index.tsx
  • app/services/tenant-type.ts
  • batch/db/mutations.ts
  • batch/db/queries.ts
  • batch/github/__tests__/buildPullRequests-filter.test.ts
  • batch/github/__tests__/release-detect.test.ts
  • batch/github/fetcher.ts
  • batch/github/model.ts
  • batch/github/pullrequest.ts
  • batch/github/store.test.ts
  • db/migrations/tenant/20260319120000_add_last_activity_at.sql
  • db/tenant.sql
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/services/tenant-type.ts
  • app/routes/$orgSlug/settings/repositories/$repository/$pull/index.tsx
  • db/tenant.sql
  • batch/github/tests/buildPullRequests-filter.test.ts

coji and others added 2 commits March 19, 2026 09:40
Apply LOWER() to company_github_users.login in the backfill query
for defensive case-insensitive matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- updateSort fallback 'asc' → 'desc' to match SortSchema default
- trackLatest: use map.get() result directly instead of redundant ?? ''

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coji coji merged commit e8227fc into main Mar 19, 2026
5 checks passed
@coji coji deleted the feat/migrate-excluded-users-to-bot-type branch March 19, 2026 00:42
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.

excludedUsersをcompanyGithubUsers.typeに統合する

1 participant