From eff23ea4c3c62b500dcc3a343168458c845c8de9 Mon Sep 17 00:00:00 2001 From: Pfassmeyer <59696207+Pfassmeyer@users.noreply.github.com> Date: Thu, 20 Aug 2026 17:02:36 +0200 Subject: [PATCH] =?UTF-8?q?fix(deriver):=20word-boundary=20matching=20in?= =?UTF-8?q?=20violatesPrivacy=20=E2=80=94=20short=20People=20slugs=20block?= =?UTF-8?q?ed=20every=20hypothesis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LIFEOS/TOOLS/LearningPatternSynthesis.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/LifeOS/install/LIFEOS/TOOLS/LearningPatternSynthesis.ts b/LifeOS/install/LIFEOS/TOOLS/LearningPatternSynthesis.ts index 80ea29146f..e3171bb8c9 100755 --- a/LifeOS/install/LIFEOS/TOOLS/LearningPatternSynthesis.ts +++ b/LifeOS/install/LIFEOS/TOOLS/LearningPatternSynthesis.ts @@ -442,12 +442,24 @@ function listPeopleSlugs(): Set { } function violatesPrivacy(text: string, peopleSlugs: Set): boolean { + // Word-boundary matching, not substring. KNOWLEDGE/People can legitimately + // contain very short slugs (an initial, a nickname); with `includes()`, a + // single-letter slug matches almost any text, so every candidate hypothesis + // is privacy-blocked and the deriver silently never emits — the failure mode + // is a clean `emitted=0`, indistinguishable from "nothing to report". + // Boundary matching preserves the guard's intent: claims that actually name + // a person still block. const norm = text.toLowerCase(); for (const slug of peopleSlugs) { - // Slug typically `firstname-lastname`; check for both joined and space-separated. - const flat = slug.replace(/-/g, ""); - const spaced = slug.replace(/-/g, " "); - if (norm.includes(spaced) || norm.includes(flat)) return true; + // Slug may be `firstname-lastname` or `firstname_lastname`; check the + // separator-normalized and joined forms. + const spaced = slug.replace(/[-_]/g, " ").trim(); + const flat = slug.replace(/[-_]/g, ""); + for (const needle of new Set([spaced, flat])) { + if (!needle) continue; + const esc = needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + if (new RegExp(`\\b${esc}\\b`, "i").test(norm)) return true; + } } return false; }