feat: round-memory врезки записи памяти кругов в живую оркестрацию SP2c-1 ч2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -472,6 +472,17 @@ async function main() {
|
||||
let taskId = null;
|
||||
try { taskId = loadTaskId({ sessionId: (event && event.session_id) || 'unknown', runtimeDir: runtimeDir(), fsImpl: fsDefault }); } catch { taskId = null; }
|
||||
const n = bumpJudgeNoGo({ taskId, sessionId: (event && event.session_id) || 'unknown', blocked: isNoGo });
|
||||
// SP2c-1: дословное замечание судьи на NO-GO в дорожку judge. Best-effort, fail-quiet.
|
||||
if (isNoGo) {
|
||||
try {
|
||||
const fp = String((event && event.tool_input && event.tool_input.file_path) || '');
|
||||
const stage = SPEC_PATH_RE.test(fp) ? 'spec' : (PLAN_PATH_RE.test(fp) ? 'plan' : null);
|
||||
if (stage) {
|
||||
const rm = await import('./round-memory-record.mjs');
|
||||
rm.recordSideObjection(taskId, stage, 'judge', formatJudgeObjection(result.verdict));
|
||||
}
|
||||
} catch { /* fail-quiet */ }
|
||||
}
|
||||
if (isNoGo && n >= JUDGE_ESCALATE_AFTER) {
|
||||
const planContent = String((event && event.tool_input && event.tool_input.content) ?? '');
|
||||
result = { ...result, message: buildJudgeArbitrationMessage(result.verdict, planContent, n) };
|
||||
|
||||
@@ -232,6 +232,17 @@ async function main() {
|
||||
// Фаза 1 (Р2): на NO-GO/degraded — ПОЛНЫЙ текст доходит до контроллера через рабочий
|
||||
// exit-2 канал (подтверждён Фазой 0). На 3-м NO-GO — карточка арбитража.
|
||||
const planContent = String((event.tool_input && event.tool_input.content) ?? '');
|
||||
// SP2c-1: память кругов — снимок версии (наставник раньше судьи в цепочке) +
|
||||
// дословное замечание наставника на NO-GO. Best-effort, fail-quiet.
|
||||
try {
|
||||
const fp = String((event.tool_input && event.tool_input.file_path) || '');
|
||||
const stage = SPEC_PATH_RE.test(fp) ? 'spec' : (PLAN_PATH_RE.test(fp) ? 'plan' : null);
|
||||
if (stage) {
|
||||
const rm = await import('./round-memory-record.mjs');
|
||||
rm.recordArtifact(res.taskId, stage, planContent);
|
||||
if (blocked) rm.recordSideObjection(res.taskId, stage, 'mentor', formatMentorObjection(res));
|
||||
}
|
||||
} catch { /* fail-quiet */ }
|
||||
const decision = decideMentorObjection({ res, planContent, n });
|
||||
// Способ B (Task 2.2): наставник НЕ печатает. На GO — записывает подписанное одобрение
|
||||
// (mentor-GO, binding plan_hash); печать сделает судья (хук ПОСЛЕ) при валидном mentor-GO.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
/** round-memory-record — orchestration-помощник записи памяти кругов (SP2c-1).
|
||||
* Цель — минимизировать врезки в discipline-source: одна точка пишет снимок версии артефакта
|
||||
* и доводы контроллера последнего круга «## Переговоры» по дорожкам M/J. Объекция стороны
|
||||
* (NO-GO) пишется отдельным вызовом recordObjection прямо из стора. Fail-quiet. */
|
||||
import { recordVersion, recordArg } from './round-memory-store.mjs';
|
||||
* (NO-GO) пишется через recordSideObjection. Fail-quiet. */
|
||||
import { recordVersion, recordArg, recordObjection } from './round-memory-store.mjs';
|
||||
import { parseNegotiationSection } from './negotiation-section.mjs';
|
||||
|
||||
/** На запись артефакта: снимок версии + доводы ПОСЛЕДНЕГО круга по дорожкам M/J.
|
||||
@@ -20,3 +20,13 @@ export function recordArtifact(taskId, stage, content, baseDir) {
|
||||
return true;
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
/** На объекцию стороны (NO-GO): дословное замечание в дорожку side∈{mentor,judge}.
|
||||
* Пустой текст не пишется. Любая ошибка → no-op (false), не кидает. */
|
||||
export function recordSideObjection(taskId, stage, side, text, baseDir) {
|
||||
try {
|
||||
const t = String(text == null ? '' : text);
|
||||
if (!t) return false;
|
||||
return recordObjection(taskId, stage, side, t, baseDir);
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest';
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { recordArtifact } from './round-memory-record.mjs';
|
||||
import { getVersions, getArgs } from './round-memory-store.mjs';
|
||||
import { recordArtifact, recordSideObjection } from './round-memory-record.mjs';
|
||||
import { getVersions, getArgs, getObjections } from './round-memory-store.mjs';
|
||||
|
||||
const dir = () => mkdtempSync(join(tmpdir(), 'rmrec-'));
|
||||
|
||||
@@ -35,3 +35,20 @@ describe('recordArtifact', () => {
|
||||
expect(() => recordArtifact('t1', 'plan', null, '\0bad')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('recordSideObjection', () => {
|
||||
it('пишет дословное замечание стороны в дорожку', () => {
|
||||
const d = dir();
|
||||
recordSideObjection('t1', 'plan', 'judge', 'замечание судьи', d);
|
||||
expect(getObjections('t1', 'plan', 'judge', d)).toEqual(['замечание судьи']);
|
||||
});
|
||||
it('пустой текст не пишется', () => {
|
||||
const d = dir();
|
||||
recordSideObjection('t1', 'plan', 'mentor', '', d);
|
||||
recordSideObjection('t1', 'plan', 'mentor', null, d);
|
||||
expect(getObjections('t1', 'plan', 'mentor', d)).toEqual([]);
|
||||
});
|
||||
it('fail-quiet на мусоре не кидает', () => {
|
||||
expect(() => recordSideObjection('t1', 'plan', 'judge', 'x', '\0bad')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user