fix(observer): routing-detector ignorit PASTED-citaty /node (P1 E/G, 4-y FP-klass)
Novyy uzkiy stripPastedContext (fenced+blockquote only) pered detekciey. NE reuse stripQuotedContext (tot stripaet inline - FN). Sohraneno FP-smeshchenie routingGate (SE-1). Regressiya 3185 passed.
This commit is contained in:
@@ -35,11 +35,27 @@ export function loadKnownNodes(path = KNOWN_NODES_PATH) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Снять ТОЛЬКО PASTED-контекст: fenced-блоки и markdown-blockquote-строки.
|
||||
* НАМЕРЕННО НЕ трогает inline-backticks/кавычки/guillemets — в отличие от
|
||||
* stripQuotedContext (tools/enforce-rationalization-audit.mjs), у которого
|
||||
* ПРОТИВОПОЛОЖНОЕ безопасное направление (FN-safe). Здесь гейт routingGate
|
||||
* FP-safe: inline-форма `/x` должна оставаться директивой (over-detection
|
||||
* дешевле тихой потери governance-сигнала; sharp-edges SE-1).
|
||||
*/
|
||||
export function stripPastedContext(text) {
|
||||
if (typeof text !== 'string') return '';
|
||||
let s = text;
|
||||
s = s.replace(/```[\s\S]*?```/g, ' '); // fenced blocks
|
||||
s = s.replace(/^[ \t]*(?:>|>).*$/gm, ' '); // markdown blockquote lines
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{directed: boolean, node: string|null}}
|
||||
*/
|
||||
export function detectMethodDirected(promptText, knownNodes) {
|
||||
const text = String(promptText || '').toLowerCase();
|
||||
const text = stripPastedContext(String(promptText || '')).toLowerCase();
|
||||
for (const node of knownNodes || []) {
|
||||
const n = String(node).toLowerCase();
|
||||
if (!n) continue;
|
||||
|
||||
@@ -48,3 +48,37 @@ describe('loadKnownNodes', () => {
|
||||
expect(loadKnownNodes('tools/does-not-exist.txt')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
import { stripPastedContext } from './observer-routing-detector.mjs';
|
||||
|
||||
describe('stripPastedContext (узкий: fenced + blockquote)', () => {
|
||||
it('removes fenced blocks', () => {
|
||||
expect(stripPastedContext('a\n```\n/x here\n```\nb')).not.toContain('/x');
|
||||
});
|
||||
it('removes blockquote lines', () => {
|
||||
expect(stripPastedContext('> /x cited\nkeep')).not.toContain('/x');
|
||||
});
|
||||
it('keeps inline backticks (FP-safe bias)', () => {
|
||||
expect(stripPastedContext('use `/x` now')).toContain('/x');
|
||||
});
|
||||
it('is non-string safe', () => {
|
||||
expect(stripPastedContext(null)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectMethodDirected — PASTED-context guard (4-й FP-класс)', () => {
|
||||
it('does NOT flag a /node inside a fenced code block', () => {
|
||||
const t = 'смотри лог:\n```\nуже использовал /brainstorming тут\n```\nчто думаешь?';
|
||||
expect(detectMethodDirected(t, NODES).directed).toBe(false);
|
||||
});
|
||||
it('does NOT flag a blockquote-cited directive', () => {
|
||||
const t = '> используй systematic-debugging здесь\nэто цитата из старого чата';
|
||||
expect(detectMethodDirected(t, NODES).directed).toBe(false);
|
||||
});
|
||||
it('STILL flags an inline /slash directive (FP-safe bias preserved)', () => {
|
||||
expect(detectMethodDirected('сделай через `/brainstorming`', NODES).directed).toBe(true);
|
||||
});
|
||||
it('STILL flags a plain-prose directive', () => {
|
||||
expect(detectMethodDirected('запусти discovery-interview', NODES).directed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user