fix(context): require a name or trigger anchor for implicit admission

This commit is contained in:
montjeffrey
2026-10-07 16:16:57 +00:00
parent bd13428151
commit deeb27d87b
4 changed files with 20 additions and 2 deletions
+2
View File
@@ -171,12 +171,14 @@ function searchRetrieval(index, query, { limit = 5 } = {}) {
.slice(0, limit)
.map(([document, score]) => {
const matched = [...new Set(queryTokens)].filter(term => document.weighted.has(term));
const anchorTerms = matched.filter(term => document.fields.name.includes(term) || document.fields.triggers.includes(term));
const exact = anchored.has(document);
return { id: document.entry.id, score: Math.round(score * 10000) / 10000, exact,
exactAlias: exact ? anchored.get(document) : undefined,
dense: Math.round((dense.get(document) || 0) * 10000) / 10000,
bm25: Math.round((bm25.get(document) || 0) * 10000) / 10000,
matchedTerms: matched,
anchorTerms,
description: document.entry.description.slice(0, 2048),
descriptionTruncated: document.entry.description.length > 2048 };
});
+8 -1
View File
@@ -26,9 +26,14 @@ const AUTO_ADMIT_MARGIN = 1.5;
const FALLBACK_MIN_BM25 = 12;
const FALLBACK_MIN_TERMS = 2;
const FALLBACK_MARGIN = 1.1;
// Both tiers also require at least one matched term in the skill's name or
// curated triggers. Description-only overlap on common words (two, both, same)
// can clear the score bars without naming the skill's subject.
const MIN_ANCHOR_TERMS = 1;
// v4: an explicit empty proposal (decline) is honored; the tier-2 fallback no
// longer overrides declines at the launch/selection call sites.
const ROUTING_POLICY_VERSION = 4;
// v5: implicit admission requires a name or trigger anchor term.
const ROUTING_POLICY_VERSION = 5;
const TASK_KEYS = new Set(['sessionId', 'taskId', 'revision', 'phase', 'query', 'explicitIds', 'proposedIds', 'noWorkflow']);
function validateTask(task) {
@@ -210,6 +215,7 @@ function resolveTaskContext({ repoRoot = DEFAULT_REPO_ROOT, task, profileId = 'l
const top = candidates[0];
const second = candidates[1];
if (top.bm25 >= AUTO_ADMIT_MIN_BM25 && top.matchedTerms.length >= AUTO_ADMIT_MIN_TERMS
&& top.anchorTerms.length >= MIN_ANCHOR_TERMS
&& (!second || top.bm25 >= AUTO_ADMIT_MARGIN * (second.bm25 || 0))) {
autoSelection = { id: top.id, bm25: top.bm25, matchedTerms: top.matchedTerms.length, exact: false };
}
@@ -221,6 +227,7 @@ function resolveTaskContext({ repoRoot = DEFAULT_REPO_ROOT, task, profileId = 'l
const top = candidates[0];
const second = candidates[1];
if (top.bm25 >= FALLBACK_MIN_BM25 && top.matchedTerms.length >= FALLBACK_MIN_TERMS
&& top.anchorTerms.length >= MIN_ANCHOR_TERMS
&& (!second || top.bm25 >= FALLBACK_MARGIN * (second.bm25 || 0))) {
fallback = { id: top.id, bm25: top.bm25, matchedTerms: top.matchedTerms.length };
}