From 2a2ded7a53c08d50fee3e01ae1deecc972ca3607 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: Tue, 19 May 2026 07:36:21 +0300 Subject: [PATCH] =?UTF-8?q?refactor(brain):=20C1=20L1-watcher=20=E2=80=94?= =?UTF-8?q?=20drop=20broken=20reverse=20drift=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the `missingInSettings` reverse check ("plugin documented in Tooling but disabled in settings.json"). It was broken by design: Tooling Прил. Н lists tools by human/group name ("Frontend Design plugin", "Trail of Bits Skills") while settings.json keys are machine IDs (`name@marketplace`) — the two namespaces never compare. The `/#\d+\s+([\w-]+(?:@[\w-]+)?)/` scan also captured the first plain word after "#NN" ("#1 PostgreSQL MCP" → "PostgreSQL"), so every run emitted ~190 lines of WARN noise. ADR-011 §6.1 specifies only the settings→Tooling direction (the L1 pattern "plugin enabled without Tooling formalization"). That is the FAIL path and is unchanged. detectDrift now returns `{ missingInTooling }` only. CLI output is a clean single line on success. Closes the cosmetic issue flagged in bffdaa9. TDD: reverse-check test replaced with `not.toHaveProperty ('missingInSettings')`; 12/12 GREEN. Smoke: node tools/l1-watcher.mjs -> exit 0, "OK — 0 drift" (no WARN block). Co-Authored-By: Claude Opus 4.7 (1M context) --- tools/l1-watcher.mjs | 17 ++++++----------- tools/l1-watcher.test.mjs | 8 +++----- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tools/l1-watcher.mjs b/tools/l1-watcher.mjs index 76891a4f..507019b8 100644 --- a/tools/l1-watcher.mjs +++ b/tools/l1-watcher.mjs @@ -20,6 +20,11 @@ export function parseAliases(raw) { return out; } +// Detects the L1 pattern (ADR-011 §6.1): a plugin enabled in settings.json +// that has no formalization in Tooling Прил. Н. Only the settings→Tooling +// direction is checked — the reverse ("documented but disabled") cannot be +// computed reliably because Tooling lists tools by human/group name while +// settings.json keys are machine IDs (`name@marketplace`). export function detectDrift(settings, toolingText, aliases = {}) { const enabled = Object.entries(settings.enabledPlugins || {}) .filter(([, v]) => v === true) @@ -32,13 +37,7 @@ export function detectDrift(settings, toolingText, aliases = {}) { if (alias && toolingText.includes(alias)) return false; return true; }); - const inToolingButNotSettings = []; - const toolingNumbered = toolingText.match(/#\d+\s+([\w-]+(?:@[\w-]+)?)/g) || []; - const toolingPluginNames = toolingNumbered.map((s) => s.split(/\s+/)[1]); - for (const p of toolingPluginNames) { - if (!enabled.includes(p)) inToolingButNotSettings.push(p); - } - return { missingInTooling, missingInSettings: inToolingButNotSettings }; + return { missingInTooling }; } function loadFileMaybe(path) { @@ -71,10 +70,6 @@ if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/l1-watcher console.error(`If the plugin is referenced in Tooling under a group/human name, add an alias to tools/.l1-watcher-aliases.txt.`); process.exit(1); } - if (drift.missingInSettings.length > 0) { - console.warn(`[l1-watcher] WARN — plugins in Tooling but disabled in settings:`); - drift.missingInSettings.forEach((p) => console.warn(` - ${p}`)); - } console.log(`[l1-watcher] OK — 0 drift`); process.exit(0); } diff --git a/tools/l1-watcher.test.mjs b/tools/l1-watcher.test.mjs index 9150bd89..9ef2b819 100644 --- a/tools/l1-watcher.test.mjs +++ b/tools/l1-watcher.test.mjs @@ -7,22 +7,20 @@ describe('detectDrift', () => { const tooling = 'Описание #56 foo@org интегрирован.'; const drift = detectDrift(settings, tooling); expect(drift.missingInTooling).toEqual(['bar@org']); - expect(drift.missingInSettings).toEqual([]); }); - it('finds plugins in tooling but not in settings', () => { + it('reports only the settings→Tooling direction (no reverse check)', () => { const settings = { enabledPlugins: { 'foo@org': true } }; const tooling = '#56 foo@org. #57 baz@org включён.'; const drift = detectDrift(settings, tooling); - expect(drift.missingInSettings).toEqual(['baz@org']); + expect(drift).not.toHaveProperty('missingInSettings'); }); - it('returns empty arrays when in sync', () => { + it('returns empty missingInTooling when in sync', () => { const settings = { enabledPlugins: { 'foo@org': true } }; const tooling = '#56 foo@org описан.'; const drift = detectDrift(settings, tooling); expect(drift.missingInTooling).toEqual([]); - expect(drift.missingInSettings).toEqual([]); }); it('handles disabled plugins (value false)', () => {