diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index 715c222..60b94d4 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -86,6 +86,7 @@ const kbWritePathEnabled = ref(true); // code embeddings sit on a much higher similarity floor than prose, so 0.55 let // unrelated code through (#2223). Shares top-k, not the threshold. const kbWritePathThreshold = ref("0.68"); +const kbRuleHintThreshold = ref("0.72"); // Near-duplicate report floors, one per record kind (services/dedup.py). // Snippets are single-chunk, so their floor sits below the 0.90 write-time // gate and catches what it lets through. Notes/tasks are scored at chunk @@ -148,12 +149,17 @@ async function saveKbInject() { // Same `|| default` reasoning: falling back to 0 would surface every // snippet in the corpus on every edit, which is the failure this knob fixes. const wpT = Math.min(1, Math.max(0, Number(kbWritePathThreshold.value) || 0.68)); + // Same `|| default` reasoning again, and it bites harder here: a rule hint + // fires on every write, so a fallback of 0 would attach a standing rule to + // every edit in the session. + const rhT = Math.min(1, Math.max(0, Number(kbRuleHintThreshold.value) || 0.72)); kbInjectThreshold.value = String(t); kbInjectTopK.value = String(k); kbDupThresholdSnippet.value = String(dupSnip); kbDupThresholdNote.value = String(dupNote); kbDupThresholdTask.value = String(dupTask); kbWritePathThreshold.value = String(wpT); + kbRuleHintThreshold.value = String(rhT); savingKbInject.value = true; kbInjectSaved.value = false; try { @@ -166,6 +172,10 @@ async function saveKbInject() { // measurements that split them. kb_writepath_enabled: kbWritePathEnabled.value ? 'true' : 'false', kb_writepath_threshold: String(wpT), + // A THIRD corpus with a third bar — see RULEHINT_DEFAULT_THRESHOLD + // in services/plugin_context.py for why rules cannot share the + // code threshold any more than code could share the prose one. + kb_rulehint_threshold: String(rhT), kb_duplicate_threshold_snippet: String(dupSnip), kb_duplicate_threshold_note: String(dupNote), kb_duplicate_threshold_task: String(dupTask), @@ -611,6 +621,9 @@ onMounted(async () => { kbInjectTopK.value = allSettings.kb_autoinject_top_k; } kbWritePathEnabled.value = allSettings.kb_writepath_enabled !== "false"; + if (allSettings.kb_rulehint_threshold !== undefined) { + kbRuleHintThreshold.value = allSettings.kb_rulehint_threshold; + } if (allSettings.kb_writepath_threshold !== undefined) { kbWritePathThreshold.value = allSettings.kb_writepath_threshold; } @@ -1456,6 +1469,29 @@ async function deleteUser(userId: number) { location, not by resemblance.

+
+ + +

+ The same hint can mention a standing rule whose trigger resembles what's + being written — only rules marked conditional, since always-on + ones are already loaded. Stricter again than the threshold above, because + there are far fewer rules than snippets: with a small set, something + always ranks first, so the bar has to carry more of the judgement. + Raise it if rules keep arriving unread; lower it if a rule you needed + never showed up. Settings → check the pull-through in + retrieval_telemetry to see which is happening. +

+