From 5d3e29669b7b3dc2626aba2c8c4a4c322ce21665 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:22:16 +0300 Subject: [PATCH] feat(observer): parallel_session +OR pre-flight git fetch heuristic (Task 13 PIVOT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes brain-retro 2026-05-20 #13 PIVOT — additive to F1 (parallel session sessions session). F1 narrowed parallel_session to tool_result-only to fix live FP. This Task adds OR-clause: Bash command containing 'git fetch && git log HEAD..origin/...' (Pravila §15.2 pre-flight) is a strong signal that the operator expects parallel sessions. Does NOT overwrite F1 — both signals coexist via OR. 4 new vitest tests, 319/319 GREEN. Co-Authored-By: Claude Opus 4.7 (1M context) --- tools/observer-transcript-parser.mjs | 26 ++++++++++-- tools/observer-transcript-parser.test.mjs | 50 +++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/tools/observer-transcript-parser.mjs b/tools/observer-transcript-parser.mjs index 7959b1df..748f3b03 100644 --- a/tools/observer-transcript-parser.mjs +++ b/tools/observer-transcript-parser.mjs @@ -207,13 +207,33 @@ export function extractEnvironment(allEntries, turnStartIdx) { // Scope NARROWED to tool_result content (real command output / Bash stderr): prose // mentions in user prompts / assistant text — including analysis text that // references collision phrases — must not trigger. Fixes live FP (episode line 20). - const parallel_session = /чужой staged|foreign git index|index\.lock|another git process/i.test( - collectToolResultText(turn) - ); + const parallel_session = + /чужой staged|foreign git index|index\.lock|another git process/i.test(collectToolResultText(turn)) + || hasPreFlightFetch(turn); return { economy_level, model, post_compaction, session_turn, parallel_session }; } +/** + * Pravila §15.2 pre-flight signal (Task 13 PIVOT): Bash-команда turn'а + * содержит `git fetch ... && git log HEAD..origin/main ...` — это hard-rule + * pre-flight sync перед правкой нормативки в параллельных сессиях. Сильный + * сигнал «заказчик ожидает параллельных сессий», аддитивный к F1 collision + * detector (parallel_session). Не overwrite — OR-clause. + */ +function hasPreFlightFetch(turn) { + for (const e of turn || []) { + const content = e && e.message && Array.isArray(e.message.content) ? e.message.content : []; + for (const b of content) { + if (b && b.type === 'tool_use' && b.name === 'Bash' && b.input) { + const cmd = String(b.input.command || ''); + if (/git\s+fetch[^|&;]*&&[^|&;]*git\s+log\s+HEAD\.\.origin\//i.test(cmd)) return true; + } + } + } + return false; +} + /** * Collect text content from tool_result blocks in the turn — the only surface * trusted for parallel_session collision evidence (see extractEnvironment). diff --git a/tools/observer-transcript-parser.test.mjs b/tools/observer-transcript-parser.test.mjs index 23b8c0a6..a7df1560 100644 --- a/tools/observer-transcript-parser.test.mjs +++ b/tools/observer-transcript-parser.test.mjs @@ -1433,3 +1433,53 @@ describe('parseTranscript — subagent_invoked events (Task 12)', () => { expect(subs[0].model).toBe('haiku'); }); }); + +describe('parallel_session — pre-flight OR clause (Task 13 PIVOT)', () => { + it('is true when Bash ran "git fetch && git log HEAD..origin/main"', () => { + const transcript = [ + JSON.stringify({ sessionId: 's' }), + JSON.stringify({ type: 'user', message: { role: 'user', content: 'pre-flight' }, uuid: 'u1', timestamp: '2026-05-20T00:00:00Z' }), + JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: [ + { type: 'tool_use', id: 't1', name: 'Bash', input: { command: 'git fetch && git log HEAD..origin/main --oneline' } } + ] }, uuid: 'u2', timestamp: '2026-05-20T00:00:01Z' }), + ].join('\n'); + const ep = parseTranscript(transcript); + expect(ep.environment.parallel_session).toBe(true); + }); + it('is true with origin/ via HEAD..origin/feat', () => { + const transcript = [ + JSON.stringify({ sessionId: 's' }), + JSON.stringify({ type: 'user', message: { role: 'user', content: 'sync' }, uuid: 'u1', timestamp: '2026-05-20T00:00:00Z' }), + JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: [ + { type: 'tool_use', id: 't1', name: 'Bash', input: { command: 'git fetch origin && git log HEAD..origin/feat/x --oneline' } } + ] }, uuid: 'u2', timestamp: '2026-05-20T00:00:01Z' }), + ].join('\n'); + const ep = parseTranscript(transcript); + expect(ep.environment.parallel_session).toBe(true); + }); + it('F1 still works — collision text in tool_result triggers', () => { + const transcript = [ + JSON.stringify({ sessionId: 's' }), + JSON.stringify({ type: 'user', message: { role: 'user', content: 'go' }, uuid: 'u1', timestamp: '2026-05-20T00:00:00Z' }), + JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: [ + { type: 'tool_use', id: 't1', name: 'Bash', input: { command: 'git commit -am ok' } } + ] }, uuid: 'u2', timestamp: '2026-05-20T00:00:01Z' }), + JSON.stringify({ type: 'user', message: { role: 'user', content: [ + { type: 'tool_result', tool_use_id: 't1', content: 'fatal: index.lock exists' } + ] }, uuid: 'u3', timestamp: '2026-05-20T00:00:02Z' }), + ].join('\n'); + const ep = parseTranscript(transcript); + expect(ep.environment.parallel_session).toBe(true); + }); + it('false on regular Bash without pre-flight or collision', () => { + const transcript = [ + JSON.stringify({ sessionId: 's' }), + JSON.stringify({ type: 'user', message: { role: 'user', content: 'go' }, uuid: 'u1', timestamp: '2026-05-20T00:00:00Z' }), + JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: [ + { type: 'tool_use', id: 't1', name: 'Bash', input: { command: 'npm run test:tools' } } + ] }, uuid: 'u2', timestamp: '2026-05-20T00:00:01Z' }), + ].join('\n'); + const ep = parseTranscript(transcript); + expect(ep.environment.parallel_session).toBe(false); + }); +});