Skip to content

feat: manual source URL intake (landing UI + server intake)#38

Merged
sasazaki1994 merged 1 commit intomainfrom
codex/add-optional-source-url-input-to-landing-page
May 8, 2026
Merged

feat: manual source URL intake (landing UI + server intake)#38
sasazaki1994 merged 1 commit intomainfrom
codex/add-optional-source-url-input-to-landing-page

Conversation

@sasazaki1994
Copy link
Copy Markdown
Owner

@sasazaki1994 sasazaki1994 commented May 8, 2026

Motivation

  • Allow users to seed an Investigation Mission with one-or-more explicit source URLs from the landing page so those sources are treated as first-class candidates during source intake.
  • Preserve existing behavior and constraints (no DB migrations, no provider schema overhaul, no RAG/embedding/crawling) while making manual URLs prioritized in the intake flow.

Description

  • Add a feature spec specs/manual-source-url-intake.md and register it in specs/README.md describing scope, UI, server action, intake, cache, and test requirements.
  • Add acceptance scenarios in acceptance/manual-source-url-intake.feature and register it in acceptance/README.md.
  • UI: add Optional source URLs textarea to the landing intake form (src/features/landing/components/question-intake.tsx) with `name=

Codex Task

Summary by CodeRabbit

リリースノート

  • 新機能

    • ホームページに手動ソースURL入力フィールドを追加しました。調査開始時に1行に1つのURLを指定できるようになり、入力されたURLは自動的に重複排除および検証されます。
  • テスト

    • E2Eテストと単体テストを追加し、URL解析、重複排除、無効入力の検証を確認しました。
  • ドキュメント

    • 手動ソースURL取込機能の仕様書とAcceptanceテストを追加しました。

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

ウォークスルー

このプルリクエストは、ユーザーがランディングページで任意のソースURLを提供できるようにする「手動ソースURL取り込み」機能を実装しています。URLは解析・検証・正規化・重複排除され、発見されたソースよりも優先されます。また、手動URLが存在する場合はラン キャッシュロックアップをバイパスします。

変更内容

手動ソースURL取り込み機能

レイヤー / ファイル 説明
型コントラクトと起源値
src/types/source-intake.ts
SourceCandidateOriginに"topic_url"が追加され、手動提供・質問抽出・発見のソース区別が可能に。
URL解析と検証
src/app/actions/manual-source-urls.ts
FormDataから手動ソースURLを解析・検証する新モジュール。行ごとに分割し、各URLを正規化し、重複を排除。無効なスキームはエラーメッセージを返す。
ランディングページUI
src/features/landing/components/question-intake.tsx
オプションのソースURL入力用テキストエリア、更新された例セクション、ローカライズ改善を追加。
サーバーアクション統合
src/app/actions/create-run.ts
parseManualSourceUrlsを呼び出して検証し、無効入力時はフォームエラーを返す。成功時は検証済みURLをプロバイダに渡す。
分析ラン プロバイダのキャッシュバイパス
src/server/analysis/create-analysis-run-from-provider.ts
manualSourceUrlsオプションを受け入れ、手動URLが存在する場合はランキャッシュロックアップをスキップ。キャッシュ保存を条件付きにし、手動URLが無い場合のみ結果を保存。
ソース取り込みURL統合と優先度
src/server/analysis/source-intake/source-intake-service.ts
manualSourceUrlsオプションを受け入れ、手動URL・質問抽出トピックURL・発見ソースを明示的な優先度ルールでマージ。正規化URLで重複排除しながら起源優先度を保持。
仕様とBDD機能ドキュメント
specs/manual-source-url-intake.md, acceptance/manual-source-url-intake.feature, acceptance/README.md, specs/README.md
完全なエンドツーエンド要件定義、UI入力、サーバー検証、統合動作、キャッシュ変更、成功/重複/エラーハンドリングパスを記述。
ユニットおよび統合テスト
tests/manual-source-urls.test.ts, tests/source-intake-integration.test.ts, e2e/home.spec.ts
parseManualSourceUrlsの空入力・正規化・重複排除・無効スキーム拒否を検証。手動URLが発見重複より優先されることを確認。ホームページのUI要素の可視性をE2Eで検証。

🎯 3 (中程度) | ⏱️ ~25分

関連する可能性があるプルリクエスト

  • sasazaki1994/TraceMap#24: buildSourceIntakeFromQuestionとSourceCandidateOrigin型をURLマージ・優先度付けで修正するため直接関連。
  • sasazaki1994/TraceMap#23: このPRはソース取り込みパイプラインを拡張してmanualSourceUrlsを受け入れ、優先度を付けるため関連。
  • sasazaki1994/TraceMap#29: QuestionIntakeコンポーネントと例セクション/ランディングマークアップの同じファイルを修正するため関連。

🐰 ウサギが言う: URLを手で入力して、
パイプラインに流す喜び!
重複は消えて、優先度つけて、
ソース調査は今日も楽し♨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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 プルリクエストのタイトルは、主な変更内容「手動ソースURL取得機能の追加(ランディングUIおよびサーバー側取得)」を明確かつ簡潔に要約しており、変更セット全体の目的をよく反映しています。
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.

✏️ 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 codex/add-optional-source-url-input-to-landing-page

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
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/features/landing/components/question-intake.tsx (1)

62-74: ⚡ Quick win

アクセシビリティ: ヘルパーテキストが textarea に紐づいていない

ヘルパーテキスト(Lines 62–64 の <p>)が aria-describedby でテキストエリアに関連付けられていないため、スクリーンリーダーがこの説明を入力フィールドと対応付けられません。

♿ 修正案
-        <p className="muted" style={{ marginTop: "0.25rem", marginBottom: "0.5rem" }}>
+        <p id="source-urls-hint" className="muted" style={{ marginTop: "0.25rem", marginBottom: "0.5rem" }}>
           Add one URL per line. TraceMap will prioritize these sources when building the evidence map.
         </p>
         <textarea
           id="sourceUrls"
           name="sourceUrls"
+          aria-describedby="source-urls-hint"
           placeholder={`https://example.com/official-report
🤖 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 `@src/features/landing/components/question-intake.tsx` around lines 62 - 74,
ヘルパーテキストの<p>(現在の説明文)をテキストエリアに関連付けるために、説明要素に一意のIDを追加(例:
"sourceUrlsHelp")し、textarea(id="sourceUrls",
data-testid="manual-source-urls-input")に aria-describedby="sourceUrlsHelp"
を追加してスクリーンリーダーが説明を読み上げられるようにしてください(必要なら説明要素を <p> のままにするか、役割を明示するために <div
role="note"> に変更しても可)。
src/app/actions/manual-source-urls.ts (1)

22-25: 💤 Low value

エラー時にどの URL が無効か特定できない

normalizeSourceUrl が失敗した場合、汎用メッセージを返すため、複数 URL を入力しているユーザーが問題箇所を特定できません。例えば、エラーメッセージに無効な行の内容や行番号を含めることで UX を改善できます。

-    if (normalized.kind !== "ok") {
-      return { kind: "error", message: MANUAL_SOURCE_URLS_ERROR_MESSAGE };
-    }
+    if (normalized.kind !== "ok") {
+      return { kind: "error", message: `${MANUAL_SOURCE_URLS_ERROR_MESSAGE} (入力値: "${line}")` };
+    }

ただし、エラーメッセージを定数として export している都合上、定数と動的メッセージを分ける設計変更が必要になる点に注意してください。

🤖 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 `@src/app/actions/manual-source-urls.ts` around lines 22 - 25, When
normalizeSourceUrl(line) returns an error, replace the opaque return using
MANUAL_SOURCE_URLS_ERROR_MESSAGE with a dynamic error that includes which input
failed (e.g., the line content and/or line index) and any normalized error
detail; keep the exported MANUAL_SOURCE_URLS_ERROR_MESSAGE as the stable
constant and compose a new message at the call site (in the code handling
normalizeSourceUrl inside manual-source-urls.ts) such as combining the constant
with `line` and the line number or normalized.error info so users can identify
the invalid URL.
tests/manual-source-urls.test.ts (1)

8-34: ⚡ Quick win

テストカバレッジ: いくつかのエッジケースが未検証

以下のケースがテストされておらず、ドキュメントとしての価値を高める余地があります。

  1. Windows 改行 (\r\n): split(/\r?\n/) で処理されていますが、テストがありません。
  2. URL 間の空白行: 複数の空行を挟んだ入力でも正しく動作するか未検証。
  3. File オブジェクト入力: FormDataEntryValuestring | File であり、File が渡された場合に空リストが返ることを明示するテストがあると、意図した動作として文書化されます。
✅ 追加テスト案
+  it("handles windows line endings", () => {
+    const result = parseManualSourceUrls("https://example.com/a\r\nhttps://example.com/b");
+    expect(result).toEqual({ kind: "ok", manualSourceUrls: ["https://example.com/a", "https://example.com/b"] });
+  });
+
+  it("ignores blank lines between valid urls", () => {
+    const result = parseManualSourceUrls("https://example.com/a\n\nhttps://example.com/b");
+    expect(result).toEqual({ kind: "ok", manualSourceUrls: ["https://example.com/a", "https://example.com/b"] });
+  });
+
+  it("returns empty list when a File is passed", () => {
+    const file = new File(["content"], "file.txt");
+    expect(parseManualSourceUrls(file)).toEqual({ kind: "ok", manualSourceUrls: [] });
+  });
🤖 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 `@tests/manual-source-urls.test.ts` around lines 8 - 34, Add tests to cover the
missing edge cases for parseManualSourceUrls: (1) a Windows newline case using
"\r\n" to ensure split(/\r?\n/) handles CRLF, (2) input containing multiple
blank lines between URLs to ensure empty lines are ignored and deduplication
still works, and (3) passing a File (simulate FormDataEntryValue as a File-like
value) to confirm parseManualSourceUrls returns { kind: "ok", manualSourceUrls:
[] } for non-string inputs; use the existing test structure and assertions
(check result.kind and manualSourceUrls or equality with
MANUAL_SOURCE_URLS_ERROR_MESSAGE where appropriate) to keep behavior documented.
🤖 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.

Nitpick comments:
In `@src/app/actions/manual-source-urls.ts`:
- Around line 22-25: When normalizeSourceUrl(line) returns an error, replace the
opaque return using MANUAL_SOURCE_URLS_ERROR_MESSAGE with a dynamic error that
includes which input failed (e.g., the line content and/or line index) and any
normalized error detail; keep the exported MANUAL_SOURCE_URLS_ERROR_MESSAGE as
the stable constant and compose a new message at the call site (in the code
handling normalizeSourceUrl inside manual-source-urls.ts) such as combining the
constant with `line` and the line number or normalized.error info so users can
identify the invalid URL.

In `@src/features/landing/components/question-intake.tsx`:
- Around line 62-74: ヘルパーテキストの<p>(現在の説明文)をテキストエリアに関連付けるために、説明要素に一意のIDを追加(例:
"sourceUrlsHelp")し、textarea(id="sourceUrls",
data-testid="manual-source-urls-input")に aria-describedby="sourceUrlsHelp"
を追加してスクリーンリーダーが説明を読み上げられるようにしてください(必要なら説明要素を <p> のままにするか、役割を明示するために <div
role="note"> に変更しても可)。

In `@tests/manual-source-urls.test.ts`:
- Around line 8-34: Add tests to cover the missing edge cases for
parseManualSourceUrls: (1) a Windows newline case using "\r\n" to ensure
split(/\r?\n/) handles CRLF, (2) input containing multiple blank lines between
URLs to ensure empty lines are ignored and deduplication still works, and (3)
passing a File (simulate FormDataEntryValue as a File-like value) to confirm
parseManualSourceUrls returns { kind: "ok", manualSourceUrls: [] } for
non-string inputs; use the existing test structure and assertions (check
result.kind and manualSourceUrls or equality with
MANUAL_SOURCE_URLS_ERROR_MESSAGE where appropriate) to keep behavior documented.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e23bdbf-1df9-4349-8ec7-71cbb24f1e80

📥 Commits

Reviewing files that changed from the base of the PR and between 770f08f and 1878a4c.

📒 Files selected for processing (13)
  • acceptance/README.md
  • acceptance/manual-source-url-intake.feature
  • e2e/home.spec.ts
  • specs/README.md
  • specs/manual-source-url-intake.md
  • src/app/actions/create-run.ts
  • src/app/actions/manual-source-urls.ts
  • src/features/landing/components/question-intake.tsx
  • src/server/analysis/create-analysis-run-from-provider.ts
  • src/server/analysis/source-intake/source-intake-service.ts
  • src/types/source-intake.ts
  • tests/manual-source-urls.test.ts
  • tests/source-intake-integration.test.ts

@sasazaki1994 sasazaki1994 merged commit 33b0319 into main May 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant