Skip to content

Develop - #556

Merged
hmjn023 merged 8 commits into
mainfrom
develop
Jun 29, 2026
Merged

Develop#556
hmjn023 merged 8 commits into
mainfrom
develop

Conversation

@hmjn023

@hmjn023 hmjn023 commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • 新機能
    • 検索画面に「ベクトル類似」モードを追加し、類似メディアを検索できるようになりました(件数・類似元指定)。
    • メディア詳細サイドバーからCCIPベクター抽出を開始でき、進捗/完了後に「Find Similar」へ遷移できます。
    • 管理画面に「vectors」タブを追加し、CCIPのスキャン/一括開始と自動抽出設定を提供します。
  • バグ修正
    • メディア削除・移動・一括削除時に、関連するベクターデータも適切に整理するよう改善しました。
    • ベクター検索の検索条件の保存・復元に対応しました。

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

CCIP ベクトル抽出、類似検索、vector 検索モード、ジョブ処理、LanceDB 永続化、削除連動、関連 UI と設定が追加された。OpenAPI、API クライアント、検索状態永続化、エージェントスキル文書も更新された。

Changes

CCIP ベクトル検索機能

Layer / File(s) Summary
スキル文書更新
.agents/skills/ai-service/SKILL.md, .agents/skills/job-system/SKILL.md, .agents/skills/job-system/agents/openai.yaml, .agents/skills/media-search/SKILL.md, .agents/skills/media-search/agents/openai.yaml, .agents/skills/solid-imager/SKILL.md, AGENTS.md
job-systemmedia-search のスキル文書、interface 設定、関連スキル一覧、AGENTS のスキル表を更新した。
契約と設定
packages/application/src/ports/ccip-vector-store.ts, packages/application/src/ports/index.ts, packages/application/src/ports/lancedb-dump-service.ts, packages/application/src/ports/media-processing-service.ts, packages/application/src/ports/tagging-service.ts, packages/core/src/domain/config/config-schema.ts, packages/core/src/domain/contract/ai.contract.ts, packages/core/src/domain/contract/media.contract.ts, packages/core/src/domain/interfaces/ai-client.ts, packages/core/src/domain/repositories/job-repository.ts, packages/core/src/domain/media/schemas.ts, packages/core/src/domain/search/logic.ts, packages/core/src/domain/search/schema.ts, packages/core/src/domain/tagging/schemas.ts
ICcipVectorStore、CCIP/類似検索スキーマ、vector 検索モード、検索永続化用の状態、設定フラグ、AI/media 契約、ジョブ進捗の冪等化に関する契約を追加・更新した。
CCIP サービスと保存
packages/application/src/services/ccip-vector-service.ts, packages/application/src/services/index.ts, packages/application/src/services/lancedb-dump-service.ts, packages/application/src/services/media-processing-service.ts, packages/application/src/services/tagging-service.ts, apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts, apps/server/src/infrastructure/ai/rust-ai-client.ts
CcipVectorService、LanceDB ベクターストア、CCIP 距離計算、自動 CCIP 抽出、LanceDB メディアID取得、CCIP 距離取得の実装と再エクスポートを追加した。
CCIP ジョブ処理
apps/server/src/infrastructure/jobs/ccip-jobs.ts, apps/server/src/application/services/job-dispatch-service.ts, apps/server/src/infrastructure/jobs/job-worker.ts, apps/server/src/infrastructure/jobs/tagging-jobs.ts, packages/db/src/repositories/job-repository.ts
extract_ccip_vector の dispatch と worker 分類、親子進捗更新、ジョブイベント発行、DB の incrementProgress の冪等更新、stale 再投入の除外、親ジョブ payload の拡張を追加した。
サーバー配線と削除連動
apps/server/src/application/services/directory-sync-service.ts, apps/server/src/application/services/maintenance-service.ts, apps/server/src/application/services/media-source-service.ts, apps/server/src/infrastructure/bootstrap.ts, apps/server/src/infrastructure/jobs/file-watcher-service.ts, apps/server/src/application/services/lancedb-dump-service.ts
CCIP サービスの遅延初期化、削除時のベクター削除、起動時 LanceDB 同期の差分判定、設定反映、ファイル/ソース削除時のベクター削除、readMediaIds 再エクスポートを接続した。
API ルーターと OpenAPI
apps/server/src/infrastructure/api/routers/ai-router.ts, apps/server/src/infrastructure/api/routers/media-router.ts, apps/server/src/infrastructure/api-clients/ai-api.ts, apps/server/src/infrastructure/api-clients/search-api.ts, apps/tauri/src/infrastructure/api-clients/search-api.ts, apps/server/public/openapi.json
AI と Media のルーター、API クライアント、OpenAPI に CCIP 距離、抽出状態、抽出開始、バッチ抽出、類似検索を追加した。
検索 vector モード
packages/core/src/domain/search/logic.ts, packages/core/src/domain/search/schema.ts, packages/ui/src/stores/search-store.ts, packages/ui/src/search-control-panel.tsx, packages/ui/src/hooks/use-search-page.ts, packages/ui/src/hooks/use-current-search-persistence.ts, packages/ui/src/preset-manager.tsx, apps/server/src/routes/search.tsx, apps/tauri/src/routes/search.tsx
検索 state、検索コントロール、永続化、preset 保存、検索ルートに vector モードと類似検索パラメータを追加した。
Media Sidebar の CCIP UI
packages/ui/src/media-sidebar.tsx, packages/ui/src/media-sidebar-content.tsx, apps/server/src/components/media/media-sidebar.tsx, apps/tauri/src/components/media/media-sidebar/media-sidebar-content.tsx, packages/ui/src/media-card-item.tsx, apps/server/src/components/media/media-card-item.tsx, apps/server/src/components/media/thumbnail-image.tsx, apps/tauri/src/components/media/media-card-item.tsx, apps/tauri/src/components/media/thumbnail-image.tsx
MediaSidebar に CCIP 状態、抽出開始、進捗購読、類似検索遷移、Extract / Find Similar ボタンを追加し、表示型を MediaSafe 系へ切り替えた。
Manager の vectors タブ
packages/ui/src/hooks/use-manager-page.ts, packages/ui/src/screens/manager-screen.tsx, packages/ui/src/screens/config-screen.tsx, apps/server/src/routes/manager.tsx, apps/tauri/src/routes/manager.tsx
Manager の vectors タブ、バッチ開始、スキャン、設定トグル、関連ルートの接続を追加した。
テストと補助ドキュメント
apps/server/src/tests/unit/application/services/ccip-vector-service.test.ts, apps/server/src/tests/unit/application/services/directory-sync-service.test.ts, apps/server/src/tests/unit/application/services/maintenance-service.test.ts, apps/server/src/tests/unit/infrastructure/jobs/ccip-jobs.test.ts, apps/server/src/tests/unit/infrastructure/jobs/job-worker.test.ts, apps/server/src/tests/unit/infrastructure/jobs/tagging-jobs.test.ts, .agents/skills/*, AGENTS.md
CCIP サービス、ジョブ処理、job worker、maintenance、タグ付けのテストと、エージェントスキル文書の追加・更新を行った。

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

ぴょんと跳ねて、CCIP きらり
ベクトル畑に 768 の芽
似てる画像を見つけたら
うさぎの耳も ぴんと立つ
ジョブも UI も ひとつながり
もぐもぐ、検索がもっと楽しい 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive 「Develop」は変更内容を示さないため、PRの主な変更点を把握できません。 CCIP抽出、類似検索、ジョブシステム追加などの主要変更を短く具体的に表すタイトルにしてください。
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 develop

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces CCIP (Character Contrastive Image Pretraining) vector extraction and similarity search capabilities to the application. It adds a new CcipVectorService, a LanceDB-based vector store, background jobs for extraction, and updates the UI to support triggering extraction and performing similarity searches. The review feedback highlights two important issues: first, a reactive infinite loop in media-sidebar.tsx caused by updating a SolidJS signal (ccipStatusRequestId) inside a createEffect that depends on it; second, a potential performance bottleneck in rust-ai-client.ts when executing CPU-heavy CCIP distance calculations concurrently over a large number of candidates using Promise.all, which should be throttled using asyncPool.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread apps/server/src/components/media/media-sidebar.tsx Outdated
Comment thread apps/server/src/infrastructure/ai/rust-ai-client.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (1)
packages/ui/src/hooks/use-manager-page.ts (1)

413-429: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

タブ切り替えで前モードのスキャン結果を再利用してしまいます。

scannedMediaselectedMedia が tagging / vectors で共通のままなので、Tagging でスキャンした直後に Vector Extraction へ切り替えると、その一覧を再スキャンなしでそのまま送信できます。packages/ui/src/screens/manager-screen.tsx Line 223-236 でも共有 state をそのまま開始処理に渡しているので、別モード向けの対象を誤って処理できます。activeTab()taggingvectors の間で変わるときに batch state をクリアするか、モードごとに state を分離してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/hooks/use-manager-page.ts` around lines 413 - 429, The shared
scan/selection state is being reused across tagging and vectors, so switching
tabs can carry the previous mode’s results into the next action. Update the
manager page logic in use-manager-page (especially handleScan and the
activeTab()-driven state) to clear scannedMedia and selectedMedia when switching
between tagging and vectors, or split them into per-mode state. Also ensure
manager-screen does not reuse the prior mode’s batch targets when starting
processing, so each tab only submits items scanned for that mode.
🟡 Minor comments (5)
apps/server/src/tests/unit/application/services/maintenance-service.test.ts-401-409 (1)

401-409: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

createIfUnique の検証が弱く、誤った同期ジョブの追加発行を見逃します。

今の toHaveBeenCalledWith(expect.objectContaining(...)) だけだと、期待ジョブに加えて誤った sync_lancedb_full / sync_lancedb_delta が同時に積まれても通ります。ここは呼び出し回数と最終引数まで固定した方が、この分岐テストの価値が出ます。

差分例
 			await service.performStartupChecks();

 			expect(mockQueueSourceLanceDBDelta).not.toHaveBeenCalled();
-			expect(mockJobRepo.createIfUnique).toHaveBeenCalledWith(
+			expect(mockJobRepo.createIfUnique).toHaveBeenCalledTimes(1);
+			expect(mockJobRepo.createIfUnique).toHaveBeenLastCalledWith(
 				expect.objectContaining({
 					type: "sync_lancedb_delta",
 					mediaSourceId: "source-1",
 				}),
 			);
-			expect(mockJobRepo.createIfUnique).toHaveBeenCalledWith(
+			expect(mockJobRepo.createIfUnique).toHaveBeenCalledTimes(1);
+			expect(mockJobRepo.createIfUnique).toHaveBeenLastCalledWith(
 				expect.objectContaining({
 					type: "sync_lancedb_delta",
 					mediaSourceId: "source-1",
 				}),
 			);
 			await service.performStartupChecks();

 			expect(mockQueueSourceLanceDBDelta).not.toHaveBeenCalled();
-			expect(mockJobRepo.createIfUnique).toHaveBeenCalledWith(
+			expect(mockJobRepo.createIfUnique).toHaveBeenCalledTimes(1);
+			expect(mockJobRepo.createIfUnique).toHaveBeenLastCalledWith(
 				expect.objectContaining({
 					type: "sync_lancedb_full",
 					mediaSourceId: "source-1",
 				}),
 			);

Also applies to: 452-457, 479-484

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/tests/unit/application/services/maintenance-service.test.ts`
around lines 401 - 409, The `performStartupChecks` tests are too loose because
`mockJobRepo.createIfUnique` is only checked with
`toHaveBeenCalledWith(expect.objectContaining(...))`, so extra incorrect sync
jobs can slip through. Tighten the assertions in these cases by verifying the
exact number of `createIfUnique` calls and the full job payload for the expected
`sync_lancedb_delta` path, using the existing `mockJobRepo.createIfUnique` and
`service.performStartupChecks` symbols to locate the affected tests.
apps/server/src/tests/unit/infrastructure/jobs/ccip-jobs.test.ts-88-130 (1)

88-130: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

ジョブ契約の肝心な部分が未検証です。

ここだと extract の引数取り回しと、親側 job-completed / job-progress の発火条件が固定されていません。processCcipExtractionJob の責務そのものなので、引数と親イベントの有無まで明示的に縛った方が回帰を拾えます。

差分例
-		expect(extract).toHaveBeenCalled();
+		expect(extract).toHaveBeenCalledWith(
+			"00000000-0000-4000-8000-000000000001",
+			"00000000-0000-4000-8000-000000000030",
+			false,
+		);
 		expect(jobRepository.incrementProgress).toHaveBeenCalledWith(
 			"00000000-0000-4000-8000-000000000010",
 			"00000000-0000-4000-8000-000000000020",
@@
 		expect(jobRepository.markAsCompleted).toHaveBeenCalledWith(
 			"00000000-0000-4000-8000-000000000010",
 			{ success: true },
 		);
+		expect(publishJob).toHaveBeenCalledWith("job-completed", {
+			jobId: "00000000-0000-4000-8000-000000000010",
+			message: "CCIP vector extraction completed",
+		});
@@
 		await processCcipExtractionJob({
@@
 		});
 
+		expect(extract).toHaveBeenCalledWith(
+			"00000000-0000-4000-8000-000000000001",
+			"00000000-0000-4000-8000-000000000031",
+			false,
+		);
 		expect(jobRepository.findById).not.toHaveBeenCalled();
 		expect(jobRepository.markAsCompleted).not.toHaveBeenCalled();
+		expect(publishJob).not.toHaveBeenCalledWith(
+			"job-progress",
+			expect.objectContaining({
+				jobId: "00000000-0000-4000-8000-000000000011",
+			}),
+		);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/tests/unit/infrastructure/jobs/ccip-jobs.test.ts` around
lines 88 - 130, The current tests around processCcipExtractionJob only cover a
subset of the job contract, so they should explicitly verify the extract call
arguments and the parent event behavior. Update the assertions in
ccip-jobs.test.ts to check the exact payload passed to extract, and in the
processCcipExtractionJob flow confirm when job-completed and job-progress are
emitted or skipped based on child progress counting. Use the existing symbols
processCcipExtractionJob, extract, publishJob, and
jobRepository.incrementProgress to keep the coverage tied to the real contract.
packages/ui/src/hooks/use-manager-page.ts-436-459 (1)

436-459: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

開始 API の非成功レスポンスが握りつぶされています。

handleStartBatchCcipExtraction には result.success === false または jobId 欠落時の分岐がないので、setTaggingStatus("Starting...") のまま止まり、失敗理由も表示されません。直下の handleStartBatchTagging と同じ else 分岐が必要です。

修正案
 			if (result.success && result.jobId) {
 				toast.success(result.message);
 				setTaggingStatus("Batch CCIP extraction in progress...");
 				setActiveJobId(result.jobId);
 				setScannedMedia([]);
 				setSelectedMedia(new Set<string>());
+			} else {
+				const message =
+					result.message || "Failed to start batch CCIP extraction.";
+				toast.error(message);
+				setTaggingStatus(message);
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/hooks/use-manager-page.ts` around lines 436 - 459,
handleStartBatchCcipExtraction is missing the non-success response handling that
exists in handleStartBatchTagging, so failures leave the UI stuck at
“Starting...”. Update the handleStartBatchCcipExtraction flow to explicitly
handle the case where result.success is false or result.jobId is missing, and
surface result.message to both toast and setTaggingStatus. Use the existing
handleStartBatchCcipExtraction and handleStartBatchTagging functions as the
reference points and keep the success path unchanged.
packages/ui/src/hooks/use-current-search-persistence.ts-61-75 (1)

61-75: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

ベクトル検索のソース絞り込みも保存・復元してください。

useSearchPage は vector モードでも selectedSourcemediaSourceId として使っていますが、ここでは similarityAnchorMediaId / similarityTopK しか永続化していません。再訪時に検索対象が「すべてのソース」へ戻り、同じ検索結果を再現できなくなります。

💡 修正案
 					if (current.mode === "vector") {
 						resetSearchState();
 						setSearchState({
 							mode: "vector",
+							selectedSource:
+								typeof current.selectedSource === "string"
+									? current.selectedSource
+									: "",
 							similarityAnchorMediaId:
 								typeof current.similarityAnchorMediaId === "string"
 									? current.similarityAnchorMediaId
 									: null,
 							similarityTopK:
@@
 		const presetData = {
 			value: condition,
 			sort: searchState.sortBy,
 			order: searchState.sortOrder,
 			mode: searchState.mode,
+			selectedSource: searchState.selectedSource,
 			similarityAnchorMediaId: searchState.similarityAnchorMediaId,
 			similarityTopK: searchState.similarityTopK,
 		};

Also applies to: 166-167

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/hooks/use-current-search-persistence.ts` around lines 61 -
75, Persist and restore the vector search source filter in
useCurrentSearchPersistence’s vector-mode branch, since useSearchPage uses
selectedSource as mediaSourceId even in vector mode and it is currently lost on
reload. Update the vector handling around resetSearchState/setSearchState to
include the source selection alongside similarityAnchorMediaId and
similarityTopK, and make sure the restore logic reads that value back so
revisiting the page preserves the same source-scoped search.
apps/server/src/infrastructure/ai/rust-ai-client.ts-260-287 (1)

260-287: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

返却距離数を candidates と突き合わせてください。

Line 268-287 は remote/native/fallback のどの経路でも返ってきた距離配列の件数を検証していないので、1 件でも欠けたり余分に返った場合に候補とスコアの対応が静かに壊れます。類似検索の順位付けを誤るので、返却前に distances.length === candidates.length を必ず確認した方がいいです。

修正案
 async calculateCcipDistances(
 	feature: number[],
 	candidates: number[][],
 ): Promise<number[]> {
+		const ensureDistances = (distances: number[]) => {
+			if (distances.length !== candidates.length) {
+				throw new Error(
+					`Invalid CCIP distances response: expected ${candidates.length}, got ${distances.length}`,
+				);
+			}
+			return distances;
+		};
+
 		if (this.baseUrl) {
 			if (!this.client) {
 				throw new Error("Client is not initialized (baseUrl is empty)");
 			}
 			const result = await this.client.ai.ccipDistances({
 				feature,
 				candidates,
 			});
-			return result.distances;
+			return ensureDistances(result.distances);
 		}
 
 		if (!this.baseUrl) {
 			const nativeModule: unknown = await import("dghs-imgutils-rs");
 			if (hasCcipDistances(nativeModule)) {
-				return await nativeModule.ccipDistances(feature, candidates);
+				return ensureDistances(
+					await nativeModule.ccipDistances(feature, candidates),
+				);
 			}
 		}
-		return await Promise.all(
-			candidates.map(async (candidate) => {
-				const result = await this.calculateCcipDifference(feature, candidate);
-				return result.difference;
-			}),
+		return ensureDistances(
+			await Promise.all(
+				candidates.map(async (candidate) => {
+					const result = await this.calculateCcipDifference(feature, candidate);
+					return result.difference;
+				}),
+			),
 		);
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/infrastructure/ai/rust-ai-client.ts` around lines 260 - 287,
`calculateCcipDistances` は remote/native/fallback
のどの経路でも返却される距離配列の件数を検証しておらず、`candidates`
と対応が崩れる可能性があります。`this.client.ai.ccipDistances`、`nativeModule.ccipDistances`、および
`Promise.all` のいずれの結果でも、返却前に距離配列の長さが `candidates.length`
と一致することを確認し、`calculateCcipDistances` 内で不一致時は例外にしてください。
🧹 Nitpick comments (1)
apps/server/src/infrastructure/jobs/tagging-jobs.ts (1)

58-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

親ジョブ payload スキーマを共通化してください。

ccip-jobs.ts 側にも同じ親 payload の Zod schema があり、こちらは total / processedz.number() のままで検証条件がズレています。共通 schema に寄せると、進捗イベントと完了判定の契約 drift を防げます。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/infrastructure/jobs/tagging-jobs.ts` around lines 58 - 61,
親ジョブの payload スキーマ定義が `tagging-jobs.ts` と `ccip-jobs.ts` で重複しており、`total` /
`processed` の検証条件もずれているため、共通の Zod schema
にまとめて両方から参照するようにしてください。`parentPayloadSchema`
を定義している箇所を基準に、進捗イベントと完了判定で同じ契約を使うよう整理し、`processedJobIds` を含む親 payload
の検証ルールが一箇所で保守される形に修正してください。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/server/public/openapi.json`:
- Around line 616-641: The OpenAPI entry for the new POST media.searchSimilar
endpoint is missing its requestBody and is emitting a useless generic 200
schema, so regenerate or fix the OpenAPI conversion for the Media router so the
Zod input/output from searchSimilar is reflected. Update the media.searchSimilar
operation in openapi.json to include the actual request body schema and a
concrete response schema, and make sure the same issue is addressed for the
other affected Media routes referenced in the diff so client generation
preserves the CCIP contract.

In `@apps/server/src/application/services/ccip-vector-service.ts`:
- Around line 1-16: The application-level getCcipVectorService() is directly
importing and instantiating LanceDbCcipVectorStore, which reverses the clean
architecture dependency direction. Move the composition of the vector store into
bootstrap/registry wiring and update CcipVectorService to accept an
ICcipVectorStore dependency instead of creating the infrastructure
implementation itself. Keep this file limited to assembling the service from
injected dependencies via services and remove the direct
~/infrastructure/ai/lancedb-ccip-vector-store reference.

In `@apps/server/src/application/services/directory-sync-service.ts`:
- Around line 63-71: The directory sync delete flow in directory-sync-service
currently swallows CCIP vector deletion failures after MediaRepository.delete,
which can leave orphaned LanceDB vectors behind. Update the delete path around
ccipVectorService.delete so failures are not treated as a harmless warn-only
case: either enqueue a retry/recovery job or propagate/fail the operation so it
is clearly separated from a completed deletion, and keep the logging in the same
block for directory sync context.

In `@apps/server/src/application/services/job-dispatch-service.ts`:
- Around line 31-35: `job-dispatch-service` in the application layer is directly
importing the infrastructure job implementation via the dynamic import for
`processCcipExtractionJob`, which violates the dependency direction. Move the
CCIP dispatch logic out of `JobDispatchService` into an infrastructure-side
dispatcher, or change `JobDispatchService` to depend only on an
application-level port/registry of job processors. Keep `job.type ===
"extract_ccip_vector"` handled through an abstracted processor contract instead
of importing `~/infrastructure/jobs/ccip-jobs` from the application service.

In `@apps/server/src/application/services/media-source-service.ts`:
- Around line 61-63: The delete flow in media-source-service’s source removal
path can leave orphan vectors because `sourceRepo.delete` and
`ccipVectorService.deleteBySource` are executed as separate non-atomic steps.
Update the delete logic around the source-removal method in `MediaSourceService`
to use a retryable/compensating flow (for example, outbox or background job) so
both the DB source delete and vector cleanup can recover from partial failure
instead of relying on simple ordering.

In `@apps/server/src/components/media/media-sidebar.tsx`:
- Around line 131-146: The CCIP extraction flow in handleCcipExtraction can
apply a stale result after the user switches media, causing the old jobId to
overwrite the current sidebar state. Capture the current media identifier/token
at the start of handleCcipExtraction, then after startCcipExtraction resolves
verify it still matches props.media (same approach as refreshCcipStatus) before
calling setCcipStatus and setActiveCcipJobId. If the media has changed, ignore
the returned result and avoid showing the success toast for the stale job.

In `@apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts`:
- Around line 59-73: The `connection()` and `table()` methods in
`LancedbCcipVectorStore` currently cache rejected Promises, so a failed
`connect()` or `openOrCreateTable()` call will permanently poison the instance.
Update the lazy-initialization flow so `connectionPromise` and `tablePromise`
are cleared/reset when the awaited operation fails, and only keep the promise
cached after a successful resolution. Use the existing `connection()`,
`table()`, and `openOrCreateTable()` methods as the fix points.
- Around line 121-132: The upsert flow in CcipLanceDBVectorStore currently
performs a delete followed by an add inside serializeWrite, which leaves a
window where get/search/list can observe missing data and can drop the old
record if add fails. Refactor the CcipLanceDBVectorStore.upsert method to use an
atomic replacement strategy in the LanceDB table operation instead of separate
delete and add steps, keeping the record visible consistently throughout the
update.

In `@apps/server/src/infrastructure/api/routers/ai-router.ts`:
- Around line 490-519: `startBatchCcipExtraction` is creating a parent job with
`input.mediaSourceId` while the `db.query.medias.findMany` filter only checks
`input.mediaIds` and `medias.mediaType`, so mixed-source IDs can slip through.
Update the selection logic in this handler to also filter by `mediaSourceId`
when provided (and keep using `mediaIds`/`image` checks), then verify the
returned rows all belong to the same source before creating the job. If the
filtered count does not match the requested IDs or the source is inconsistent,
throw `BAD_REQUEST` from `startBatchCcipExtraction` before calling
`jobRepository.create`.

In `@apps/server/src/infrastructure/api/routers/media-router.ts`:
- Line 203: After deleteMedia, moveMedia, and bulkDeleteMedia finish, the direct
ccipVectorService.delete cleanup should not be on the critical API path because
failures there turn a completed media operation into a partial failure. Move the
vector deletion into the same application/service layer as MediaService or wrap
it in a compensating async cleanup with retry handling so the API response
reflects the primary operation outcome. Update the cleanup call sites in
media-router to avoid throwing after the main media mutation succeeds.

In `@apps/server/src/infrastructure/jobs/file-watcher-service.ts`:
- Around line 87-95: The file deletion flow in file-watcher-service leaves
orphaned vector data because MediaRepository.delete succeeds and
ccipVectorService.delete is only warned on failure. Update the remove path in
the file-watcher handler to use the same retry or compensating cleanup strategy
as directory sync, so vector deletion failures are not silently skipped. Make
the fix around the media removal logic that calls MediaRepository.delete and
ccipVectorService.delete, and ensure failures are handled in a way that
preserves storage consistency.

In `@packages/application/src/services/ccip-vector-service.ts`:
- Around line 159-164: `isCurrent()` is treating moved media as still current
because it only checks model, embedding version, and modified time. Update
`CcipVectorService.isCurrent()` to also compare `mediaSourceId` against the
record’s source so `extract()`, `getStatus()`, and source-filtered searches
don’t reuse vectors from a different source. Use the existing `CcipVectorRecord`
and `Media` fields in this method to make source identity part of the freshness
check.

In `@packages/application/src/services/media-processing-service.ts`:
- Around line 272-280: The `processMedia` flow in `MediaProcessingService` is
enqueueing duplicate `extract_ccip_vector` jobs for the same `mediaId`, so
switch this enqueue path to use `createIfUnique` instead of `create` and keep
the uniqueness keyed on the `payload.mediaId` used by `JobRepository`. Make sure
the `extract_ccip_vector` job creation path only inserts once even when
`processMedia` runs repeatedly or concurrently, and verify the CCIP job flow in
`ccip-jobs` still respects the existing duplicate suppression behavior.

In `@packages/core/src/domain/contract/ai.contract.ts`:
- Around line 53-55: `scanBatchCcipTargets` is exposing `mediaSchema` directly
from the public contract, which locks internal fields like `filePath` into the
API surface. Update `ai.contract.ts` to return a dedicated public Safe DTO
schema instead of `mediaSchema`, and make sure the corresponding
`scanBatchCcipTargets` contract type uses that new safe schema so the server
router can map from internal media data to the sanitized response.

In `@packages/core/src/domain/search/logic.ts`:
- Around line 15-16: The mode transition logic in search/logic.ts is dropping
the prior non-vector search state, so returning from vector mode cannot restore
the original filters or advanced condition. Update the mode-switching flow
around the vector branch and the state reconstruction logic in the search mode
handler so the previous mode and its fields are preserved when entering vector
mode, then restored when switching back from vector to simple/pro; use the
existing mode conversion logic in the search state functions to keep filter
values and advancedCondition intact.

In `@packages/core/src/domain/tagging/schemas.ts`:
- Around line 42-45: `ccipDistancesRequestSchema` currently accepts any-length
numeric arrays for `feature` and `candidates`, so tighten the contract to
enforce the fixed 768-dimensional vector shape at the boundary. Update the
schema in `schemas.ts` to validate that `feature` is exactly 768 numbers and
each inner array in `candidates` is also exactly 768 numbers, using the existing
`ccipDistancesRequestSchema` symbol so invalid lengths are rejected before
reaching the AI client or LanceDB.

In `@packages/ui/src/media-sidebar.tsx`:
- Around line 136-149: The CCIP extraction flow in extractCcipVector can apply a
late response to the wrong media after props.media changes. Capture the current
media key or a request token before calling props.startCcipExtraction, then
after the await verify the media is still the same before calling setCcipStatus
and setActiveCcipJobId. If the media changed, discard the result so
useCcipJobEvents keeps subscribing to the correct job.

---

Outside diff comments:
In `@packages/ui/src/hooks/use-manager-page.ts`:
- Around line 413-429: The shared scan/selection state is being reused across
tagging and vectors, so switching tabs can carry the previous mode’s results
into the next action. Update the manager page logic in use-manager-page
(especially handleScan and the activeTab()-driven state) to clear scannedMedia
and selectedMedia when switching between tagging and vectors, or split them into
per-mode state. Also ensure manager-screen does not reuse the prior mode’s batch
targets when starting processing, so each tab only submits items scanned for
that mode.

---

Minor comments:
In `@apps/server/src/infrastructure/ai/rust-ai-client.ts`:
- Around line 260-287: `calculateCcipDistances` は remote/native/fallback
のどの経路でも返却される距離配列の件数を検証しておらず、`candidates`
と対応が崩れる可能性があります。`this.client.ai.ccipDistances`、`nativeModule.ccipDistances`、および
`Promise.all` のいずれの結果でも、返却前に距離配列の長さが `candidates.length`
と一致することを確認し、`calculateCcipDistances` 内で不一致時は例外にしてください。

In `@apps/server/src/tests/unit/application/services/maintenance-service.test.ts`:
- Around line 401-409: The `performStartupChecks` tests are too loose because
`mockJobRepo.createIfUnique` is only checked with
`toHaveBeenCalledWith(expect.objectContaining(...))`, so extra incorrect sync
jobs can slip through. Tighten the assertions in these cases by verifying the
exact number of `createIfUnique` calls and the full job payload for the expected
`sync_lancedb_delta` path, using the existing `mockJobRepo.createIfUnique` and
`service.performStartupChecks` symbols to locate the affected tests.

In `@apps/server/src/tests/unit/infrastructure/jobs/ccip-jobs.test.ts`:
- Around line 88-130: The current tests around processCcipExtractionJob only
cover a subset of the job contract, so they should explicitly verify the extract
call arguments and the parent event behavior. Update the assertions in
ccip-jobs.test.ts to check the exact payload passed to extract, and in the
processCcipExtractionJob flow confirm when job-completed and job-progress are
emitted or skipped based on child progress counting. Use the existing symbols
processCcipExtractionJob, extract, publishJob, and
jobRepository.incrementProgress to keep the coverage tied to the real contract.

In `@packages/ui/src/hooks/use-current-search-persistence.ts`:
- Around line 61-75: Persist and restore the vector search source filter in
useCurrentSearchPersistence’s vector-mode branch, since useSearchPage uses
selectedSource as mediaSourceId even in vector mode and it is currently lost on
reload. Update the vector handling around resetSearchState/setSearchState to
include the source selection alongside similarityAnchorMediaId and
similarityTopK, and make sure the restore logic reads that value back so
revisiting the page preserves the same source-scoped search.

In `@packages/ui/src/hooks/use-manager-page.ts`:
- Around line 436-459: handleStartBatchCcipExtraction is missing the non-success
response handling that exists in handleStartBatchTagging, so failures leave the
UI stuck at “Starting...”. Update the handleStartBatchCcipExtraction flow to
explicitly handle the case where result.success is false or result.jobId is
missing, and surface result.message to both toast and setTaggingStatus. Use the
existing handleStartBatchCcipExtraction and handleStartBatchTagging functions as
the reference points and keep the success path unchanged.

---

Nitpick comments:
In `@apps/server/src/infrastructure/jobs/tagging-jobs.ts`:
- Around line 58-61: 親ジョブの payload スキーマ定義が `tagging-jobs.ts` と `ccip-jobs.ts`
で重複しており、`total` / `processed` の検証条件もずれているため、共通の Zod schema
にまとめて両方から参照するようにしてください。`parentPayloadSchema`
を定義している箇所を基準に、進捗イベントと完了判定で同じ契約を使うよう整理し、`processedJobIds` を含む親 payload
の検証ルールが一箇所で保守される形に修正してください。
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2185e85-2e0c-4f35-b593-a07b5b538ede

📥 Commits

Reviewing files that changed from the base of the PR and between 4d77f36 and b98726a.

📒 Files selected for processing (69)
  • .agents/skills/ai-service/SKILL.md
  • .agents/skills/job-system/SKILL.md
  • .agents/skills/job-system/agents/openai.yaml
  • .agents/skills/media-search/SKILL.md
  • .agents/skills/media-search/agents/openai.yaml
  • .agents/skills/solid-imager/SKILL.md
  • AGENTS.md
  • apps/server/public/openapi.json
  • apps/server/src/application/services/ccip-vector-service.ts
  • apps/server/src/application/services/directory-sync-service.ts
  • apps/server/src/application/services/job-dispatch-service.ts
  • apps/server/src/application/services/lancedb-dump-service.ts
  • apps/server/src/application/services/maintenance-service.ts
  • apps/server/src/application/services/media-processing-service.ts
  • apps/server/src/application/services/media-source-service.ts
  • apps/server/src/components/media/media-sidebar.tsx
  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts
  • apps/server/src/infrastructure/ai/rust-ai-client.ts
  • apps/server/src/infrastructure/api-clients/ai-api.ts
  • apps/server/src/infrastructure/api-clients/search-api.ts
  • apps/server/src/infrastructure/api/routers/ai-router.ts
  • apps/server/src/infrastructure/api/routers/media-router.ts
  • apps/server/src/infrastructure/bootstrap.ts
  • apps/server/src/infrastructure/jobs/ccip-jobs.ts
  • apps/server/src/infrastructure/jobs/file-watcher-service.ts
  • apps/server/src/infrastructure/jobs/job-worker.ts
  • apps/server/src/infrastructure/jobs/tagging-jobs.ts
  • apps/server/src/routes/manager.tsx
  • apps/server/src/routes/search.tsx
  • apps/server/src/tests/unit/application/services/ccip-vector-service.test.ts
  • apps/server/src/tests/unit/application/services/directory-sync-service.test.ts
  • apps/server/src/tests/unit/application/services/maintenance-service.test.ts
  • apps/server/src/tests/unit/infrastructure/jobs/ccip-jobs.test.ts
  • apps/server/src/tests/unit/infrastructure/jobs/job-worker.test.ts
  • apps/server/src/tests/unit/infrastructure/jobs/tagging-jobs.test.ts
  • apps/tauri/src/components/media/media-sidebar/media-sidebar-content.tsx
  • apps/tauri/src/infrastructure/api-clients/search-api.ts
  • apps/tauri/src/routes/manager.tsx
  • apps/tauri/src/routes/search.tsx
  • packages/application/src/ports/ccip-vector-store.ts
  • packages/application/src/ports/index.ts
  • packages/application/src/ports/lancedb-dump-service.ts
  • packages/application/src/ports/media-processing-service.ts
  • packages/application/src/ports/tagging-service.ts
  • packages/application/src/services/ccip-vector-service.ts
  • packages/application/src/services/index.ts
  • packages/application/src/services/lancedb-dump-service.ts
  • packages/application/src/services/media-processing-service.ts
  • packages/application/src/services/tagging-service.ts
  • packages/core/src/domain/config/config-schema.ts
  • packages/core/src/domain/contract/ai.contract.ts
  • packages/core/src/domain/contract/media.contract.ts
  • packages/core/src/domain/interfaces/ai-client.ts
  • packages/core/src/domain/media/schemas.ts
  • packages/core/src/domain/repositories/job-repository.ts
  • packages/core/src/domain/search/logic.ts
  • packages/core/src/domain/search/schema.ts
  • packages/core/src/domain/tagging/schemas.ts
  • packages/db/src/repositories/job-repository.ts
  • packages/ui/src/hooks/use-current-search-persistence.ts
  • packages/ui/src/hooks/use-manager-page.ts
  • packages/ui/src/hooks/use-search-page.ts
  • packages/ui/src/media-sidebar-content.tsx
  • packages/ui/src/media-sidebar.tsx
  • packages/ui/src/preset-manager.tsx
  • packages/ui/src/screens/config-screen.tsx
  • packages/ui/src/screens/manager-screen.tsx
  • packages/ui/src/search-control-panel.tsx
  • packages/ui/src/stores/search-store.ts

Comment thread apps/server/public/openapi.json
Comment thread apps/server/src/application/services/ccip-vector-service.ts
Comment thread apps/server/src/application/services/directory-sync-service.ts
Comment thread apps/server/src/application/services/job-dispatch-service.ts
Comment thread apps/server/src/application/services/media-source-service.ts Outdated
Comment thread packages/application/src/services/media-processing-service.ts
Comment thread packages/core/src/domain/contract/ai.contract.ts Outdated
Comment thread packages/core/src/domain/search/logic.ts Outdated
Comment thread packages/core/src/domain/tagging/schemas.ts
Comment thread packages/ui/src/media-sidebar.tsx
- media-sidebar: AbortController パターンでリアクティビティ問題を修正
- media-router: ベクター削除失敗時の try-catch 追加
- rust-ai-client: asyncPool で並列度を制限 (CPU過剰消費防止)
- lancedb-ccip-vector-store: 失敗時 Promise リセット追加
- ai-router: mediaSourceId フィルタ追加
- media-source-service: ベクター削除の try-catch 追加
- ccip-vector-service: isCurrent に mediaSourceId チェック追加
- media-processing-service: createIfUnique で重複投入防止
- ai.contract: mediaSafeSchema 使用 (filePath 除外)
- search/logic: ベクトルモード切替時の条件保持
- tagging/schemas: CCIP ベクトル長を 768 に固定
- ui/media-sidebar: メディア切り替え後の抽出結果反映を修正

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
apps/server/src/components/media/media-sidebar.tsx (1)

120-126: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

メディア切替中の抽出フラグが別メディアに漏れます。

isExtractingCcip はコンポーネント共通 state ですが、media 変更時にリセットされず、finally でも現在の media を確認せずに false に戻しています。これだと旧 media の開始リクエスト中に新しい media のボタンが無効化されたり、新しい media の抽出中に旧リクエストの finally が loading を消します。media key を capture して finally をガードし、切替時にも isExtractingCcip(false) を明示してください。

差分案
 createEffect(() => {
 	props.media.id;
 	props.media.mediaSourceId;
+	setIsExtractingCcip(false);
 	setCcipStatus("missing");
 	setActiveCcipJobId(null);
 	void refreshCcipStatus();
 });

 const handleCcipExtraction = async () => {
 	setIsExtractingCcip(true);
 	const currentMediaId = props.media.id;
+	const currentMediaSourceId = props.media.mediaSourceId;
 	try {
 		const result = await startCcipExtraction(
-			props.media.mediaSourceId,
+			currentMediaSourceId,
 			currentMediaId,
 			ccipStatus() === "ready" || ccipStatus() === "stale",
 		);
-		if (props.media.id !== currentMediaId) return;
+		if (
+			props.media.id !== currentMediaId ||
+			props.media.mediaSourceId !== currentMediaSourceId
+		)
+			return;
 		setCcipStatus("processing");
 		setActiveCcipJobId(result.jobId);
 		toast.success("CCIP vector extraction queued");
 	} catch (error) {
-		if (props.media.id !== currentMediaId) return;
+		if (
+			props.media.id !== currentMediaId ||
+			props.media.mediaSourceId !== currentMediaSourceId
+		)
+			return;
 		toast.error(`Failed to extract CCIP vector: ${getErrorMessage(error)}`);
 	} finally {
-		setIsExtractingCcip(false);
+		if (
+			props.media.id === currentMediaId &&
+			props.media.mediaSourceId === currentMediaSourceId
+		) {
+			setIsExtractingCcip(false);
+		}
 	}
 };

Also applies to: 128-145

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/components/media/media-sidebar.tsx` around lines 120 - 126,
The shared isExtractingCcip state in media-sidebar.tsx is leaking across media
switches because it is not reset on media change and the async finally path can
clear loading for a different media. Update the media change effect around
createEffect, setCcipStatus, and refreshCcipStatus to explicitly set
isExtractingCcip(false) when props.media.id or props.media.mediaSourceId
changes, and capture the current media key inside the extraction flow so the
finally block only clears loading if it still matches the same media. Use the
existing props.media, refreshCcipStatus, and isExtractingCcip symbols to keep
the guard local to the component logic.
♻️ Duplicate comments (1)
apps/server/src/infrastructure/api/routers/media-router.ts (1)

204-208: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

ベクター削除をまだ同期 API パスで待っています。

例外は握りつぶせていますが、ここで await ccipVectorService.delete(...) を残すと、ベクターストアが遅い/ハングしただけで delete / move / bulkDelete の成功応答まで巻き添えで遅延・タイムアウトします。主操作後の cleanup はジョブ化するか、成功応答後の非同期処理へ切り離した方がよいです。

Also applies to: 242-246, 301-308

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/infrastructure/api/routers/media-router.ts` around lines 204
- 208, The cleanup path in media-router still blocks the synchronous
delete/move/bulkDelete flow by awaiting ccipVectorService.delete, so successful
responses can be delayed or time out if the vector store is slow. Refactor the
post-success cleanup in the relevant handlers in media-router (the delete, move,
and bulkDelete flows) so the vector delete runs asynchronously after the main
operation completes, or is dispatched to a background job, while keeping the
existing warning logging in the catch path for failed cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/domain/media/schemas.ts`:
- Around line 157-158: `mediaSafeSchema` is using `omit({ filePath: true })`,
which can accidentally include future internal fields added to `mediaSchema`.
Update `mediaSafeSchema` in `schemas.ts` to explicitly whitelist only the public
fields using `pick(...)` or a dedicated `z.object(...)`, and keep `MediaSafe`
inferred from that safe schema. Ensure any API-facing mapping continues to use
`MediaSafe` so only explicitly allowed properties are exposed.

---

Outside diff comments:
In `@apps/server/src/components/media/media-sidebar.tsx`:
- Around line 120-126: The shared isExtractingCcip state in media-sidebar.tsx is
leaking across media switches because it is not reset on media change and the
async finally path can clear loading for a different media. Update the media
change effect around createEffect, setCcipStatus, and refreshCcipStatus to
explicitly set isExtractingCcip(false) when props.media.id or
props.media.mediaSourceId changes, and capture the current media key inside the
extraction flow so the finally block only clears loading if it still matches the
same media. Use the existing props.media, refreshCcipStatus, and
isExtractingCcip symbols to keep the guard local to the component logic.

---

Duplicate comments:
In `@apps/server/src/infrastructure/api/routers/media-router.ts`:
- Around line 204-208: The cleanup path in media-router still blocks the
synchronous delete/move/bulkDelete flow by awaiting ccipVectorService.delete, so
successful responses can be delayed or time out if the vector store is slow.
Refactor the post-success cleanup in the relevant handlers in media-router (the
delete, move, and bulkDelete flows) so the vector delete runs asynchronously
after the main operation completes, or is dispatched to a background job, while
keeping the existing warning logging in the catch path for failed cleanup.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 50e622ea-5784-4fce-97af-84de635fd02d

📥 Commits

Reviewing files that changed from the base of the PR and between b98726a and 99b4e51.

📒 Files selected for processing (13)
  • apps/server/src/application/services/media-source-service.ts
  • apps/server/src/components/media/media-sidebar.tsx
  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts
  • apps/server/src/infrastructure/ai/rust-ai-client.ts
  • apps/server/src/infrastructure/api/routers/ai-router.ts
  • apps/server/src/infrastructure/api/routers/media-router.ts
  • packages/application/src/services/ccip-vector-service.ts
  • packages/application/src/services/media-processing-service.ts
  • packages/core/src/domain/contract/ai.contract.ts
  • packages/core/src/domain/media/schemas.ts
  • packages/core/src/domain/search/logic.ts
  • packages/core/src/domain/tagging/schemas.ts
  • packages/ui/src/media-sidebar.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/server/src/application/services/media-source-service.ts
  • packages/core/src/domain/search/logic.ts
  • packages/application/src/services/media-processing-service.ts
  • packages/core/src/domain/tagging/schemas.ts
  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts
  • packages/ui/src/media-sidebar.tsx
  • apps/server/src/infrastructure/api/routers/ai-router.ts
  • packages/application/src/services/ccip-vector-service.ts

Comment thread packages/core/src/domain/media/schemas.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
packages/ui/src/hooks/use-manager-page.ts (1)

459-483: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

CCIP抽出開始の失敗分岐が抜けています。

startBatchCcipExtraction()success: false もしくは jobId なしで返った場合、この処理は何も通知せず taggingStatus"Starting..." のまま残します。handleStartBatchTagging() と同じ失敗分岐を入れないと、開始失敗が UI から判別できません。

修正案
 		const result = await actions.startBatchCcipExtraction({
 			force: forceRetag(),
 			mediaSourceId: selectedSourceId(),
 			mediaIds: Array.from(selectedMedia()),
 		});
 		if (result.success && result.jobId) {
 			toast.success(result.message);
 			setTaggingStatus("Batch CCIP extraction in progress...");
 			setActiveJobId(result.jobId);
 			setScannedMedia([]);
 			setSelectedMedia(new Set<string>());
+		} else {
+			const message =
+				result.message || "Failed to start batch CCIP extraction.";
+			toast.error(message);
+			setTaggingStatus(message);
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/hooks/use-manager-page.ts` around lines 459 - 483, The
handleStartBatchCcipExtraction flow is missing the explicit failure branch when
actions.startBatchCcipExtraction returns success: false or no jobId, so the UI
can stay stuck on "Starting...". Update this function to mirror
handleStartBatchTagging by handling the non-success case inside the result
check, showing an error toast and setting taggingStatus to an error message when
the batch CCIP extraction does not actually start.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/ui/src/hooks/use-manager-page.ts`:
- Around line 459-483: The handleStartBatchCcipExtraction flow is missing the
explicit failure branch when actions.startBatchCcipExtraction returns success:
false or no jobId, so the UI can stay stuck on "Starting...". Update this
function to mirror handleStartBatchTagging by handling the non-success case
inside the result check, showing an error toast and setting taggingStatus to an
error message when the batch CCIP extraction does not actually start.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a010c2ad-88b1-4c7b-9cc3-a0684951d056

📥 Commits

Reviewing files that changed from the base of the PR and between 99b4e51 and 3cda69e.

📒 Files selected for processing (11)
  • apps/server/src/components/media/media-card-item.tsx
  • apps/server/src/components/media/media-sidebar.tsx
  • apps/server/src/components/media/thumbnail-image.tsx
  • apps/server/src/routes/manager.tsx
  • apps/tauri/src/components/media/media-card-item.tsx
  • apps/tauri/src/components/media/thumbnail-image.tsx
  • apps/tauri/src/routes/manager.tsx
  • packages/core/src/domain/media/schemas.ts
  • packages/ui/src/hooks/use-manager-page.ts
  • packages/ui/src/media-card-item.tsx
  • packages/ui/src/screens/manager-screen.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/server/src/routes/manager.tsx
  • apps/tauri/src/routes/manager.tsx
  • packages/core/src/domain/media/schemas.ts
  • apps/server/src/components/media/media-sidebar.tsx
  • packages/ui/src/screens/manager-screen.tsx

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.

1 participant