diff --git a/tools/round-memory-store.mjs b/tools/round-memory-store.mjs index db8ef52..349eea3 100644 --- a/tools/round-memory-store.mjs +++ b/tools/round-memory-store.mjs @@ -77,3 +77,25 @@ export function clearRoundMemory(taskId, baseDir) { return true; } catch { return false; } } + +/** SP2c-2: собрать память кругов для построителя промпта (судья J-side / наставник M-side). + * side='judge' → дорожка J, свои judge-замечания, БЕЗ замечания-при-возврате (судья холодный); + * side='mentor' → дорожка M, свои mentor-замечания + ПОСЛЕДНЕЕ замечание судьи (при возврате). + * versionDiff = diff(последняя сохранённая версия, текущая); пусто пока прошлой версии нет + * (круг 1 слеп). Fail-quiet → пустая память. */ +export function buildRoundMemory({ taskId, stage, side, currentContent = '', baseDir } = {}) { + try { + const track = side === 'judge' ? 'J' : 'M'; + const versions = getVersions(taskId, stage, baseDir); + const prev = versions.length ? versions[versions.length - 1] : null; + const versionDiff = prev != null ? diffLines(prev, String(currentContent ?? '')) : ''; + const objections = getObjections(taskId, stage, side, baseDir); + const args = getArgs(taskId, stage, track, baseDir); + let judgeObjectionOnReturn = ''; + if (side === 'mentor') { + const jo = getObjections(taskId, stage, 'judge', baseDir); + if (jo.length) judgeObjectionOnReturn = jo[jo.length - 1]; + } + return { objections, args, versionDiff, judgeObjectionOnReturn }; + } catch { return { objections: [], args: [], versionDiff: '', judgeObjectionOnReturn: '' }; } +} diff --git a/tools/round-memory-store.test.mjs b/tools/round-memory-store.test.mjs index b99614d..e977cf7 100644 --- a/tools/round-memory-store.test.mjs +++ b/tools/round-memory-store.test.mjs @@ -6,6 +6,7 @@ import { recordVersion, getVersions, diffVersions, recordObjection, getObjections, recordArg, getArgs, clearRoundMemory, + buildRoundMemory, } from './round-memory-store.mjs'; const dir = () => mkdtempSync(join(tmpdir(), 'rmem-')); @@ -56,3 +57,40 @@ describe('round-memory-store', () => { expect(getVersions('t1', 'spec', '\0bad')).toEqual([]); }); }); + +describe('buildRoundMemory (SP2c-2)', () => { + it('круг 1 слеп: нет версий → versionDiff пуст, объекции/доводы пусты', () => { + const d = dir(); + const m = buildRoundMemory({ taskId: 't1', stage: 'plan', side: 'judge', currentContent: 'v1', baseDir: d }); + expect(m).toEqual({ objections: [], args: [], versionDiff: '', judgeObjectionOnReturn: '' }); + }); + it('judge: только J-дорожка + свои judge-замечания + diff против текущей, без замечания-при-возврате', () => { + const d = dir(); + recordVersion('t1', 'plan', 'строка1\nстрока2', d); + recordObjection('t1', 'plan', 'judge', 'замечание судьи 1', d); + recordObjection('t1', 'plan', 'mentor', 'замечание наставника', d); + recordArg('t1', 'plan', 'J', 'довод судье', d); + recordArg('t1', 'plan', 'M', 'довод наставнику', d); + const m = buildRoundMemory({ taskId: 't1', stage: 'plan', side: 'judge', currentContent: 'строка1\nстрока2-изм', baseDir: d }); + expect(m.objections).toEqual(['замечание судьи 1']); + expect(m.args).toEqual(['довод судье']); + expect(m.versionDiff).toContain('строка2'); + expect(m.judgeObjectionOnReturn).toBe(''); + }); + it('mentor: M-дорожка + свои mentor-замечания + ПОСЛЕДНЕЕ замечание судьи', () => { + const d = dir(); + recordVersion('t1', 'spec', 'тело', d); + recordObjection('t1', 'spec', 'mentor', 'замечание наставника 1', d); + recordObjection('t1', 'spec', 'judge', 'замечание судьи A', d); + recordObjection('t1', 'spec', 'judge', 'замечание судьи B', d); + recordArg('t1', 'spec', 'M', 'довод наставнику', d); + const m = buildRoundMemory({ taskId: 't1', stage: 'spec', side: 'mentor', currentContent: 'тело2', baseDir: d }); + expect(m.objections).toEqual(['замечание наставника 1']); + expect(m.args).toEqual(['довод наставнику']); + expect(m.judgeObjectionOnReturn).toBe('замечание судьи B'); + }); + it('fail-quiet на битом baseDir → пустая память', () => { + const m = buildRoundMemory({ taskId: 't1', stage: 'plan', side: 'judge', currentContent: 'x', baseDir: '\0bad' }); + expect(m).toEqual({ objections: [], args: [], versionDiff: '', judgeObjectionOnReturn: '' }); + }); +});