feat: Source Discovery v0.1 boundary + source intake integration#24
feat: Source Discovery v0.1 boundary + source intake integration#24sasazaki1994 merged 2 commits intomainfrom
Conversation
ウォークスルーTraceMapにソースディスカバリー機能を追加し、ユーザーが提供する研究トピックから自動的にソースURL候補を検出します。プラガブルな 変更内容ソースディスカバリー機能
推奨される関連PR
推定レビュー工数🎯 3 (中程度) | ⏱️ 約20分 詩
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
tests/source-discovery-provider.test.ts (1)
20-24: 💤 Low value
result.kindアサション後のifガードは不要ですLine 22 の
expect(result.kind).toBe("success")が成功した後、Line 23 のif (result.kind === "success")は常に真であるため、実質的に意味のないガードです。Vitest のassertによるタイプナローイングか、直接アサションするパターンへ整理すると意図が明確になります。♻️ 提案
it("respects maxResults", async () => { const result = await mockSourceDiscoveryProvider.discoverSources({ researchTopic: "Acme", maxResults: 2 }); expect(result.kind).toBe("success"); - if (result.kind === "success") expect(result.candidates).toHaveLength(2); + expect((result as { kind: "success"; candidates: unknown[] }).candidates).toHaveLength(2); });🤖 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/source-discovery-provider.test.ts` around lines 20 - 24, The test uses a redundant runtime guard after asserting result.kind === "success"; remove the if (result.kind === "success") check and directly assert the candidates length instead (e.g., after calling mockSourceDiscoveryProvider.discoverSources and expect(result.kind).toBe("success"), call expect(result.candidates).toHaveLength(2)). Update the test around the result variable and the mockSourceDiscoveryProvider.discoverSources call so the assertion is clear and concise without the unnecessary type guard.tests/source-intake-integration.test.ts (1)
16-16: 💤 Low value
candidates[0]?.originのオプショナルチェーンがデバッグを困難にします
candidatesが空の場合、?.originはundefinedを返しexpected undefined to be 'manual_url'という誤解を招くエラーメッセージになります。長さを先に確認することでエラーの原因が明確になります。♻️ 提案
+ expect(result.candidates).toHaveLength(1); expect(result.candidates[0]?.origin).toBe("manual_url");🤖 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/source-intake-integration.test.ts` at line 16, The test uses an optional chain on candidates (expect(result.candidates[0]?.origin).toBe("manual_url")) which hides whether candidates is empty; first assert the array length (e.g., expect(result.candidates.length).toBeGreaterThan(0) or toHaveLength(expected)) and then assert the first item's origin without optional chaining (expect(result.candidates[0].origin).toBe("manual_url")) so failures clearly indicate an empty array vs unexpected origin.src/server/analysis/source-discovery/resolve-source-discovery-provider.ts (1)
1-1: 💤 Low value
mockSourceDiscoveryProviderの即時インポートについてモジュールロード時に常に
mockSourceDiscoveryProviderが評価されます。モック実装が副作用のない純粋なスタブである限り実害はなく、バンドル時のツリーシェイキングで除去されますが、将来的にモックが外部依存を持つ場合に備えて遅延インポートとすることも選択肢の一つです。現時点では問題ありません。
🤖 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/server/analysis/source-discovery/resolve-source-discovery-provider.ts` at line 1, 現在の即時トップレベルインポート「mockSourceDiscoveryProvider」がモジュールロード時に常に評価されるため、将来モックが副作用や外部依存を持つ場合に備えて遅延ロードに変更してください — 具体的にはトップレベルの import を削り、resolveSourceDiscoveryProvider(または該当するファクトリ関数)内で条件が満たされたときに動的 import('…/mock-source-discovery-provider') を使って mockSourceDiscoveryProvider を読み込み、その返り値(default/名前付きエクスポート)を参照するようにします。これによりモックは実際に必要なときだけ評価され、バンドルやモジュール初期化時の副作用を防げます。
🤖 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 @.github/workflows/ci.yml:
- Around line 35-38: Replace every occurrence of the GitHub Action usage "uses:
pnpm/action-setup@v4" with the current recommended "uses: pnpm/action-setup@v6"
(you can pin to v6.0.5 if desired); there are three occurrences of "uses:
pnpm/action-setup@v4" in the workflow and you should update all three so each
job uses the v6 action, leaving the existing "with: version: 10.18.2" input
unchanged.
In `@src/server/analysis/source-intake/source-intake-service.ts`:
- Around line 13-23: discoverSources
の呼び出しが例外を投げると処理全体が中断してしまうので、discoveryProvider.discoverSources(...) を try/catch
で囲み、例外発生時は ignoredUrls に { url: "[source_discovery]", reason: error.message ||
String(error) } を push して discoveredUrls の更新をスキップし処理を継続するように修正してください(参照箇所:
discoveryProvider, discoverSources, ignoredUrls, discoveredUrls,
DEFAULT_DISCOVERY_MAX_RESULTS, question)。
---
Nitpick comments:
In `@src/server/analysis/source-discovery/resolve-source-discovery-provider.ts`:
- Line 1:
現在の即時トップレベルインポート「mockSourceDiscoveryProvider」がモジュールロード時に常に評価されるため、将来モックが副作用や外部依存を持つ場合に備えて遅延ロードに変更してください
— 具体的にはトップレベルの import
を削り、resolveSourceDiscoveryProvider(または該当するファクトリ関数)内で条件が満たされたときに動的
import('…/mock-source-discovery-provider') を使って mockSourceDiscoveryProvider
を読み込み、その返り値(default/名前付きエクスポート)を参照するようにします。これによりモックは実際に必要なときだけ評価され、バンドルやモジュール初期化時の副作用を防げます。
In `@tests/source-discovery-provider.test.ts`:
- Around line 20-24: The test uses a redundant runtime guard after asserting
result.kind === "success"; remove the if (result.kind === "success") check and
directly assert the candidates length instead (e.g., after calling
mockSourceDiscoveryProvider.discoverSources and
expect(result.kind).toBe("success"), call
expect(result.candidates).toHaveLength(2)). Update the test around the result
variable and the mockSourceDiscoveryProvider.discoverSources call so the
assertion is clear and concise without the unnecessary type guard.
In `@tests/source-intake-integration.test.ts`:
- Line 16: The test uses an optional chain on candidates
(expect(result.candidates[0]?.origin).toBe("manual_url")) which hides whether
candidates is empty; first assert the array length (e.g.,
expect(result.candidates.length).toBeGreaterThan(0) or toHaveLength(expected))
and then assert the first item's origin without optional chaining
(expect(result.candidates[0].origin).toBe("manual_url")) so failures clearly
indicate an empty array vs unexpected origin.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 90c6a534-45a5-43bb-a6a3-c7d5c906c959
📒 Files selected for processing (13)
.github/workflows/ci.ymlacceptance/README.mdacceptance/source-discovery.featurespecs/README.mdspecs/source-discovery.mdsrc/server/analysis/source-discovery/mock-source-discovery-provider.tssrc/server/analysis/source-discovery/resolve-source-discovery-provider.tssrc/server/analysis/source-discovery/source-discovery-provider.tssrc/server/analysis/source-discovery/source-discovery-service.tssrc/server/analysis/source-intake/source-intake-service.tssrc/types/source-intake.tstests/source-discovery-provider.test.tstests/source-intake-integration.test.ts
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
| with: | ||
| version: 10.18.2 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/ci.yml | head -200Repository: sasazaki1994/TraceMap
Length of output: 5740
🌐 Web query:
pnpm/action-setup v4 Node.js runtime requirements
💡 Result:
The pnpm/action-setup v4 GitHub Action does not have strict Node.js runtime requirements specified in its documentation. It explicitly states: "This action does not setup Node.js for you, use actions/setup-node yourself."[1][2] You must separately configure Node.js using actions/setup-node@v4 (or equivalent), and pnpm/action-setup will use whatever Node.js runtime is available in the environment.[1][3] Key points from official sources: - v2 had compatibility issues with newer Node.js versions, prompting upgrade to v3+ (including v4).[1][2] - Recent v4 releases (v4.3.0, v4.4.0) updated the action's internal runtime to Node.js 24.[4][5] - Older v4 versions likely used Node.js 20 or earlier, but since v4.4.0, it runs on Node.js 24.[4] - Dependencies include @types/node ^20.x, indicating development against Node 20+.[6] - Example workflows use Node.js 20: uses: actions/setup-node@v4 with node-version: 20.[3][7] - Standalone mode (standalone: true) installs @pnpm/exe, a self-bundled executable that does not require Node.js.[1][2] - For pnpm versions like 11.x, Node.js 22+ is recommended due to pnpm requirements, but this is for the installed pnpm, not the action itself.[8] In practice, v4 works with Node.js 20+ in user workflows, with updates ensuring compatibility up to Node.js 24.[4][9] No minimum Node.js version is enforced by the action; compatibility depends on the pnpm version installed and GitHub Actions runner (ubuntu-latest supports Node 20-24 as of 2026).[9]
Citations:
- 1: https://github.com/pnpm/action-setup/tree/v4
- 2: https://github.com/pnpm/action-setup
- 3: https://pnpm.io/continuous-integration
- 4: Bump pnpm/action-setup from 4 to 6 actions/setup-node#1528
- 5: https://github.com/pnpm/action-setup/releases
- 6: pnpm/action-setup@v4.2.0...v4.3.0
- 7: https://context7.com/pnpm/action-setup/llms.txt
- 8: https://www.infoq.com/news/2026/04/pnpm-11-rc-release/
- 9: https://github.com/lerna/lerna-changelog/actions/runs/23063368021
🌐 Web query:
GitHub Actions Node.js 20 deprecation Node.js 24 June 2026
💡 Result:
As of today (2026-05-07), GitHub Actions is deprecating Node.js 20 and migrating the default JavaScript runtime to Node.js 24. 1) Key deprecation/migration dates (Node.js 20 -> Node.js 24) - GitHub’s published deprecation notice says that starting June 2, 2026, GitHub Actions runners begin using Node.js 24 by default. [1] - In that same notice, GitHub also states you can temporarily keep Node.js 20 after June 2, 2026 by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true, but it will only work until Node 20 is removed later in the fall of 2026. [1] - GitHub notes that this deprecation process was started because Node.js 20 reaches end-of-life (EOL) in April 2026. [1] 2) How to react in your workflows - If you use JavaScript actions without pinning to a version that supports Node 24, you should upgrade the action versions. GitHub’s notice explicitly recommends updating actions to run on Node 24 and checking whether newer versions are available. [1] - A common way to control your app/test Node.js version (separate from the runtime used by the GitHub-provided “JavaScript actions” on the runner) is to use actions/setup-node with node-version. [2] 3) What your workflows might be telling you right now - Users commonly see a warning that Node.js 20 actions are deprecated and that actions will be forced to run with Node.js 24 by default starting June 2, 2026; the warning also mentions how to opt into Node 24 early (FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true) and how to opt out of the default using ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true. [3] If you tell me which specific GitHub Actions (e.g., actions/checkout@..., actions/setup-node@..., github/codeql-action@...) your workflows use, I can map them to what you should upgrade to avoid Node 20 runtime warnings.
Citations:
- 1: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
- 2: https://docs.github.com/en/actions/language-and-framework-guides/using-nodejs-with-github-actions
- 3: Node JS 20 Deprecated on GitHub Actions ariga/atlas#3696
🌐 Web query:
pnpm/action-setup v6 current version recommended
💡 Result:
Recommended: use pnpm/action-setup@v6, pinned to the latest v6 patch release: pnpm/action-setup@v6.0.5 (published 2026-05-02). [1] Example (best practice: pin to an exact v6.x.y): uses: pnpm/action-setup@v6.0.5 Or, to always stay on the latest v6 line (un-pinned): uses: pnpm/action-setup@v6 Notes: - The action’s README shows usage with pnpm/action-setup@v6, and documents that you can omit the version input to use the version from the package.json packageManager field. [1] - There is a known issue where specifying package_json_file and relying on packageManager in that file may not work as expected on some v6 versions; the issue suggests a workaround of passing version explicitly. [2]
Citations:
- 1: https://github.com/pnpm/action-setup/
- 2: action-setup@v6 does not install pnpm version specified in
package_json_filepnpm/action-setup#227
pnpm/action-setup を v4 から v6 へアップグレードしてください
pnpm/action-setup@v6 が現在の推奨バージョンです(公式リポジトリのREADMEで明記されており、2026年5月2日に v6.0.5 がリリースされています)。3つのジョブすべてで同じアップグレードが必要です。
🔧 推奨される修正
- uses: pnpm/action-setup@v4
+ uses: pnpm/action-setup@v63箇所(Line 36、Line 95、Line 154)すべてで同様の変更が必要です。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.18.2 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10.18.2 |
🤖 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 @.github/workflows/ci.yml around lines 35 - 38, Replace every occurrence of
the GitHub Action usage "uses: pnpm/action-setup@v4" with the current
recommended "uses: pnpm/action-setup@v6" (you can pin to v6.0.5 if desired);
there are three occurrences of "uses: pnpm/action-setup@v4" in the workflow and
you should update all three so each job uses the v6 action, leaving the existing
"with: version: 10.18.2" input unchanged.
| if (discoveryProvider.id !== "disabled") { | ||
| const discovery = await discoveryProvider.discoverSources({ | ||
| researchTopic: question, | ||
| maxResults: DEFAULT_DISCOVERY_MAX_RESULTS, | ||
| }); | ||
| if (discovery.kind === "failure") { | ||
| ignoredUrls.push({ url: "[source_discovery]", reason: discovery.errorMessage }); | ||
| } else { | ||
| discoveredUrls = discovery.candidates.map((candidate) => candidate.url); | ||
| } | ||
| } |
There was a problem hiding this comment.
discoverSources の例外時に調査を継続できるようにしてください。
現在は kind: "failure" のみを処理しており、discoverSources が throw するとこの関数全体が reject して調査フローが中断します。ここも ignoredUrls に記録して継続するのが安全です。
修正例
let discoveredUrls: string[] = [];
if (discoveryProvider.id !== "disabled") {
- const discovery = await discoveryProvider.discoverSources({
- researchTopic: question,
- maxResults: DEFAULT_DISCOVERY_MAX_RESULTS,
- });
- if (discovery.kind === "failure") {
- ignoredUrls.push({ url: "[source_discovery]", reason: discovery.errorMessage });
- } else {
- discoveredUrls = discovery.candidates.map((candidate) => candidate.url);
- }
+ try {
+ const discovery = await discoveryProvider.discoverSources({
+ researchTopic: question,
+ maxResults: DEFAULT_DISCOVERY_MAX_RESULTS,
+ });
+ if (discovery.kind === "failure") {
+ ignoredUrls.push({ url: "[source_discovery]", reason: discovery.errorMessage });
+ } else {
+ discoveredUrls = discovery.candidates.map((candidate) => candidate.url);
+ }
+ } catch (error) {
+ ignoredUrls.push({
+ url: "[source_discovery]",
+ reason: error instanceof Error ? error.message : String(error),
+ });
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (discoveryProvider.id !== "disabled") { | |
| const discovery = await discoveryProvider.discoverSources({ | |
| researchTopic: question, | |
| maxResults: DEFAULT_DISCOVERY_MAX_RESULTS, | |
| }); | |
| if (discovery.kind === "failure") { | |
| ignoredUrls.push({ url: "[source_discovery]", reason: discovery.errorMessage }); | |
| } else { | |
| discoveredUrls = discovery.candidates.map((candidate) => candidate.url); | |
| } | |
| } | |
| if (discoveryProvider.id !== "disabled") { | |
| try { | |
| const discovery = await discoveryProvider.discoverSources({ | |
| researchTopic: question, | |
| maxResults: DEFAULT_DISCOVERY_MAX_RESULTS, | |
| }); | |
| if (discovery.kind === "failure") { | |
| ignoredUrls.push({ url: "[source_discovery]", reason: discovery.errorMessage }); | |
| } else { | |
| discoveredUrls = discovery.candidates.map((candidate) => candidate.url); | |
| } | |
| } catch (error) { | |
| ignoredUrls.push({ | |
| url: "[source_discovery]", | |
| reason: error instanceof Error ? error.message : String(error), | |
| }); | |
| } | |
| } |
🤖 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/server/analysis/source-intake/source-intake-service.ts` around lines 13 -
23, discoverSources
の呼び出しが例外を投げると処理全体が中断してしまうので、discoveryProvider.discoverSources(...) を try/catch
で囲み、例外発生時は ignoredUrls に { url: "[source_discovery]", reason: error.message ||
String(error) } を push して discoveredUrls の更新をスキップし処理を継続するように修正してください(参照箇所:
discoveryProvider, discoverSources, ignoredUrls, discoveredUrls,
DEFAULT_DISCOVERY_MAX_RESULTS, question)。
Motivation
Description
specs/source-discovery.mdand acceptanceacceptance/source-discovery.featureto document purpose, scope, provider strategy, dedup rules, error handling, security and test requirements.SourceDiscoveryProviderboundary and types insrc/server/analysis/source-discovery/source-discovery-provider.tswith a resolverresolveSourceDiscoveryProviderand constants insource-discovery-service.ts.mockdiscovery provider atsrc/server/analysis/source-discovery/mock-source-discovery-provider.tsand adisableddefault provider via the resolver, controllable withTRACEMAP_SOURCE_DISCOVERY_PROVIDER.buildSourceIntakeFromQuestioninsrc/server/analysis/source-intake/source-intake-service.tsto: extract manual URLs, call discovery when enabled, merge manual+discovered URLs (manual-first), normalize/dedupe by normalized URL, resolve cache/fetch metadata viaresolveSourceCacheForUrl, and produceSourceCandidate[]while recordingignoredUrlsfor failures.origin?: "manual_url" | "discovered"toSourceCandidateinsrc/types/source-intake.ts(optional, non-breaking).DEFAULT_DISCOVERY_MAX_RESULTSandDEFAULT_SOURCE_CANDIDATE_MAX_RESULTS(both set to 5) to control cost surface.tests/source-discovery-provider.test.tsandtests/source-intake-integration.test.ts, and updatedspecs/README.mdandacceptance/README.mdto list the new spec/acceptance files.Testing
pnpm lintand it succeeded.pnpm typecheckand it succeeded.pnpm testand all tests passed: "Test Files 32 passed (32), Tests 146 passed (146)" and the new unit tests (provider + intake integration) also passed.pnpm buildand Next.js compiled successfully.pnpm exec prisma validatefailed due to missingDATABASE_URLin the environment (local validation error), so Prisma validation could not be completed in this CI-less environment.Codex Task
Summary by CodeRabbit
リリースノート
新機能
テスト