From f8b32a7d3ae6816c846b6d83efcc3f3ab7c0e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 20 May 2026 13:17:11 +0300 Subject: [PATCH] feat(observer): extend classifyPromptSignal vocabulary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes brain-retro 2026-05-20 #9 — добавлены маркеры: - correction: 'не совсем', 'другое|другая', 'не сходится', 'wrong direction' - approval: 'класс', 'хорошо', 'принято', 'well done', 'nice' - new_task (prefix): 'теперь', 'далее', 'следующее', 'next', 'now' NB на JS \b с Cyrillic: \b matches word↔non-word boundary, но Cyrillic chars не word-chars в JS RegExp default → \b после русского слова никогда не fires. Решение: substring-match для русских correction-маркеров; lookahead с явными разделителями для start-of-prompt new_task маркеров. 11 new vitest tests, 301/301 GREEN. Co-Authored-By: Claude Opus 4.7 (1M context) --- tools/observer-transcript-parser.mjs | 5 ++-- tools/observer-transcript-parser.test.mjs | 36 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/tools/observer-transcript-parser.mjs b/tools/observer-transcript-parser.mjs index 5afb7c74..7932dcb6 100644 --- a/tools/observer-transcript-parser.mjs +++ b/tools/observer-transcript-parser.mjs @@ -402,15 +402,16 @@ export function extractAskUserQuestionEvents(turn) { export function classifyPromptSignal(text) { const t = String(text || '').toLowerCase().trim(); if ( - /не то\b|не так\b|переделай|отбой|\bстоп\b|почему ты|неверно|не верно|это не |не работает|не правильн|сломал|опять|снова не|всё ещё|все ещё|все еще|верни как|откат|\brevert\b|\bundo\b|still not|doesn'?t work|does not work|\bwrong\b/.test( + /не совсем|другое|другая|не сходится|wrong direction|не то\b|не так\b|переделай|отбой|\bстоп\b|почему ты|неверно|не верно|это не |не работает|не правильн|сломал|опять|снова не|всё ещё|все ещё|все еще|верни как|откат|\brevert\b|\bundo\b|still not|doesn'?t work|does not work|\bwrong\b/.test( t ) ) { return 'correction'; } - if (/^(ок|окей|ok|спасибо|супер|отлично|готово|дальше|идеально)([,\s]|$)/.test(t)) { + if (/^(ок|окей|ok|спасибо|супер|отлично|готово|дальше|идеально|класс|хорошо|принято|well done|\bnice\b)([,\s]|$)/.test(t)) { return 'approval'; } + if (/^(?:теперь|далее|следующее)(?=\s|[,.!?:;]|$)|^next\b|^now\b/.test(t)) return 'new_task'; if (classifyTask(t) !== 'other' && t.length > 15) return 'new_task'; return 'neutral'; } diff --git a/tools/observer-transcript-parser.test.mjs b/tools/observer-transcript-parser.test.mjs index dc482920..ac4bd0f8 100644 --- a/tools/observer-transcript-parser.test.mjs +++ b/tools/observer-transcript-parser.test.mjs @@ -1291,3 +1291,39 @@ describe('promptText strips blocks (Task 8)', () => { expect(ep.primary_rationale.task_classification).toBe('refactor'); }); }); + +describe('classifyPromptSignal — extended dictionary (Task 9)', () => { + it('detects "не совсем" as correction', () => { + expect(classifyPromptSignal('не совсем то')).toBe('correction'); + }); + it('detects "wrong direction" as correction', () => { + expect(classifyPromptSignal('wrong direction here')).toBe('correction'); + }); + it('detects "другое" as correction', () => { + expect(classifyPromptSignal('другое решение нужно')).toBe('correction'); + }); + it('detects "класс" as approval', () => { + expect(classifyPromptSignal('класс')).toBe('approval'); + }); + it('detects "well done" as approval', () => { + expect(classifyPromptSignal('well done')).toBe('approval'); + }); + it('detects "nice" as approval', () => { + expect(classifyPromptSignal('nice')).toBe('approval'); + }); + it('detects "теперь" prefix as new_task', () => { + expect(classifyPromptSignal('теперь сделай биллинг')).toBe('new_task'); + }); + it('detects "далее" prefix as new_task', () => { + expect(classifyPromptSignal('далее переходим к настройкам')).toBe('new_task'); + }); + it('detects "next" prefix as new_task', () => { + expect(classifyPromptSignal('next step please')).toBe('new_task'); + }); + it('preserves existing — neutral for nondescript text', () => { + expect(classifyPromptSignal('некий случайный текст')).toBe('neutral'); + }); + it('preserves existing — correction "переделай"', () => { + expect(classifyPromptSignal('переделай это')).toBe('correction'); + }); +});