This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 3f1b8af39a1b099bc8bd9d2eaa420ab7d359a01f Author: Stephen Mallette <[email protected]> AuthorDate: Fri Oct 9 15:44:06 2026 -0400 Cut noise from tinker-review discussion discovery Module and package names no longer count as search keywords, and an explicitly linked dev@ thread arrives with its subject and opening message. Assisted-by: Claude Code:claude-opus-5-5 --- .../tinker-review/scripts/discovery/discussions.js | 28 ++++++++++++++++++---- .skills/tinker-review/scripts/review.js | 13 ++++++++-- .skills/tinker-review/test/discovery.test.js | 14 +++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/.skills/tinker-review/scripts/discovery/discussions.js b/.skills/tinker-review/scripts/discovery/discussions.js index 1fd5ebc9b5..aff7c158b5 100644 --- a/.skills/tinker-review/scripts/discovery/discussions.js +++ b/.skills/tinker-review/scripts/discovery/discussions.js @@ -229,6 +229,26 @@ async function resolveExplicitProposals(repoPath, proposalPaths) { return out; } +// An explicitly linked dev@ thread arrives as a bare URL. Fill in its subject +// and opening message from the archive's thread API, so the agent has something +// to read; on failure the placeholder title stays. +async function fetchDevThread(thread) { + const id = thread.url.match(/\/thread\/([^/?#]+)/)?.[1]; + if (!id) return thread; + try { + const data = JSON.parse(await httpGet(`https://lists.apache.org/api/thread.lua?id=${encodeURIComponent(id)}`)); + const t = data.thread || {}; + return { + ...thread, + title: t.subject || thread.title, + body: (t.body || "").slice(0, 1000), + date: t.epoch ? new Date(t.epoch * 1000).toISOString().slice(0, 10) : undefined, + }; + } catch { + return thread; + } +} + function extractLinksFromText(text) { const jiraRefs = [...new Set([...text.matchAll(TINKERPOP_JIRA_PATTERN)].map((m) => m[0]))]; const devListRefs = [...new Set([...text.matchAll(DEV_LIST_LINK_PATTERN)].map((m) => m[0]))]; @@ -257,14 +277,14 @@ async function followLinks(discussions) { for (const url of devListRefs) { if (discussions.some((d) => d.url === url)) continue; - secondary.push({ + secondary.push(await fetchDevThread({ url, source: "devlist", title: "(referenced thread)", body: "", found_in: `${disc.source}_body`, found_via: disc.id || disc.url, - }); + })); } } @@ -311,13 +331,13 @@ export async function discoverDiscussions(params) { // --- Dev list (direct) --- const devListLinks = [...new Set([...allText.matchAll(DEV_LIST_LINK_PATTERN)].map((m) => m[0]))]; - const explicitDevList = devListLinks.map((url) => ({ + const explicitDevList = await Promise.all(devListLinks.map((url) => fetchDevThread({ url, source: "devlist", title: "(linked thread)", body: "", found_in: "pr", - })); + }))); let searchedDevList = []; let devListSearchPerformed = false; diff --git a/.skills/tinker-review/scripts/review.js b/.skills/tinker-review/scripts/review.js index 905004268e..a179f0ff70 100644 --- a/.skills/tinker-review/scripts/review.js +++ b/.skills/tinker-review/scripts/review.js @@ -210,14 +210,23 @@ function tokenizeName(name) { // words (the human statement of intent) lead so they can't be truncated by the // cap, then distinctive tokens from changed-file basenames. Structural names, // generic verbs, and repo-ubiquitous tokens are filtered out so we don't search -// on noise like "NOTICE" or "gremlin-server". Exported for testing. +// on noise like "NOTICE" or "gremlin-server". So are location words: any token +// of a changed file's directory path (a module such as tinkergraph-gremlin, a +// package such as structure/storage) says where the change is, not what it is +// about, and matches every proposal that mentions the module. Exported for testing. export function extractKeywords(changedFiles, prTitle) { + const location = new Set(); + for (const file of changedFiles) { + for (const dir of file.split("/").slice(0, -1)) { + for (const tok of dir.toLowerCase().split(/[^a-z0-9]+/)) location.add(tok); + } + } const keywords = []; const seen = new Set(); const push = (word) => { const k = word.toLowerCase(); if (k.length < 3 || seen.has(k)) return; - if (GENERIC_WORDS.has(k) || UBIQUITOUS_TOKENS.has(k)) return; + if (GENERIC_WORDS.has(k) || UBIQUITOUS_TOKENS.has(k) || location.has(k)) return; seen.add(k); keywords.push(k); }; diff --git a/.skills/tinker-review/test/discovery.test.js b/.skills/tinker-review/test/discovery.test.js index b2fd2b4950..67208bc84f 100644 --- a/.skills/tinker-review/test/discovery.test.js +++ b/.skills/tinker-review/test/discovery.test.js @@ -95,3 +95,17 @@ test("a title keyword matches at INFERRED strength; a lone body mention does not }, ); }); + +test("extractKeywords drops words that only name where the change is (module and package dirs)", () => { + const kws = extractKeywords( + [ + "tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/storage/GraphBinaryStorage.java", + "tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerStorageGraph.java", + ], + "TINKERPOP-3282 TinkerStorageGraph", + ); + assert.ok(!kws.includes("tinkergraph"), "module name excluded"); + assert.ok(!kws.includes("storage"), "package name excluded"); + assert.ok(kws.includes("tinkerstoragegraph"), "title topic kept"); + assert.ok(kws.includes("binary"), "a basename token that is not a location is kept"); +});
