diff --git a/AI/lib/search.js b/AI/lib/search.js index 8875dc5..0383f8c 100644 --- a/AI/lib/search.js +++ b/AI/lib/search.js @@ -98,9 +98,27 @@ async function search(opts) { const rows = db.prepare(sql).all(...args); if (!rows.length) { db.close(); return { results: [], intents: [], scanned: 0 }; } - const qv = await embedQuery(url, embedModel, query); - const intents = section ? [] : detectIntent(query); - const wantDoc = /\b(how do i|steps|procedure|setup|install|troubleshoot|guide)\b/i.test(query); + const qv = await embedQuery(url, embedModel, query); + let intents = section ? [] : detectIntent(query); + + // "What is Varaverk" and "what is arr_sync.sh" are not the same question, and the PURPOSE + // intent cannot tell them apart — it fires on both and boosts every PURPOSE block in the + // repository at once. There are a couple of hundred, each genuinely describing the purpose of + // something, and each a short sentence containing the word Varaverk. The project's own README + // then loses to a script that migrates storage modes, because a paragraph is more diluted + // than a one-line summary. + // + // A question that names the project and no component inside it is asking about the whole, so + // PURPOSE is precisely the wrong section to promote. Dropping only that intent, rather than + // all of them, leaves "why was Varaverk built this way" still routed to DESIGN PRINCIPLES. + const namesProject = /\bvaraverk\b/i.test(query); + const namesComponent = /\b[\w.-]+\.(sh|php|js)\b|\b[A-Z][A-Z0-9]*(_[A-Z0-9]+)+\b/.test(query); + const projectLevel = namesProject && !namesComponent; + if (projectLevel) intents = intents.filter(s => s !== 'PURPOSE'); + + // The same question wants the top-level prose, which is what the doc kinds are. + const wantDoc = projectLevel + || /\b(how do i|steps|procedure|setup|install|troubleshoot|guide)\b/i.test(query); const scored = rows.map(r => { let s = dot(qv, blobToVec(r.vector));