feat: round-memory загрузка roundMemory J-side в хуке судьи SP2c-2 инкремент 3
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -118,7 +118,16 @@ export async function runJudgeGate(event, deps = {}) {
|
||||
// «Оба строго» (2026-06-12): СВОЙ ключ судьи ROUTER_JUDGE_LLM_KEY, общий не фолбэк.
|
||||
const apiKey = deps.apiKey !== undefined ? deps.apiKey : resolveJudgeLlmKey();
|
||||
const requiredLenses = requiredLensesFor(functionName);
|
||||
const promptArgs = { product: g.product, goal: g.goal, cards: g.cards };
|
||||
// SP2c-2: память кругов J-side (свои judge-замечания + J-доводы + diff; судья холодный —
|
||||
// без замечания-при-возврате). roundMemoryImpl грузит из стора (await — годится sync-тест и
|
||||
// async-прод); нет инъекции → круг слеп ({}). buildJudgePrompt уже рендерит roundMemory.
|
||||
const stage = functionName === 'gate1' ? 'spec' : 'plan';
|
||||
const rmContent = String((event && event.tool_input && event.tool_input.content) ?? '');
|
||||
let roundMemory = {};
|
||||
if (typeof deps.roundMemoryImpl === 'function') {
|
||||
try { roundMemory = (await deps.roundMemoryImpl({ stage, content: rmContent })) || {}; } catch { roundMemory = {}; }
|
||||
}
|
||||
const promptArgs = { product: g.product, goal: g.goal, cards: g.cards, roundMemory };
|
||||
const raw = await callJudgeModel({ functionName, requiredLenses, promptArgs, apiKey, model: deps.model, transport: deps.transport });
|
||||
if (raw && raw.unavailable) {
|
||||
// M7: причина недоступности протекает в вердикт → лог-WARN + seal-запись её фиксируют.
|
||||
@@ -458,7 +467,17 @@ async function main() {
|
||||
};
|
||||
let result;
|
||||
try {
|
||||
result = await runJudgeTurn(event, { mode, nowMs: Date.now(), onWiredSeal: sealTurnProd, mentorApproved }); // inert/shadow/live-block внутри; nowMs → at в seal/verdict/warn (M7)
|
||||
result = await runJudgeTurn(event, {
|
||||
mode, nowMs: Date.now(), onWiredSeal: sealTurnProd, mentorApproved,
|
||||
// SP2c-2: реальный загрузчик памяти кругов J-side из стора (taskId — тот же, что
|
||||
// сохранил наставник до судьи; side='judge' холодный). Динамический импорт, fail-quiet внутри.
|
||||
roundMemoryImpl: async ({ stage, content }) => {
|
||||
let taskId = null;
|
||||
try { taskId = loadTaskId({ sessionId: (event && event.session_id) || 'unknown', runtimeDir: runtimeDir(), fsImpl: fsDefault }); } catch { taskId = null; }
|
||||
const { buildRoundMemory } = await import('./round-memory-store.mjs');
|
||||
return buildRoundMemory({ taskId, stage, side: 'judge', currentContent: content, baseDir: runtimeDir() });
|
||||
},
|
||||
}); // inert/shadow/live-block внутри; nowMs → at в seal/verdict/warn (M7)
|
||||
} catch { exitDecision({ block: mode === 'live-block' }); return; } // fail-CLOSE только в live-block
|
||||
// M7 эскалация (round-control C-12): подряд идущие NO-GO судьи. allow → сброс. После 3-го подряд —
|
||||
// сообщение «ЭСКАЛАЦИЯ ВЛАДЕЛЬЦУ» (судья сам выходит на владельца; продавить — escape, который судья
|
||||
|
||||
@@ -109,6 +109,28 @@ describe('runJudgeGate (async) — рубильник + детект + преф
|
||||
expect(calls).toBe(1);
|
||||
expect(r.wired).toBe(true);
|
||||
});
|
||||
|
||||
// SP2c-2: судья J-side получает roundMemory (свои judge-замечания + J-доводы + diff) в промпт.
|
||||
it('SP2c-2: roundMemoryImpl (plan) → J-память в промпте судьи (stage plan)', async () => {
|
||||
const prompts = [];
|
||||
await runJudgeGate(planEv(), {
|
||||
judgeActiveImpl: () => true, apiKey: 'K', mentorApproved: () => true,
|
||||
transport: async (p) => { prompts.push(p); return okText; },
|
||||
roundMemoryImpl: ({ stage }) => ({ objections: [`память судьи ${stage}`] }),
|
||||
});
|
||||
expect(prompts).toHaveLength(1);
|
||||
expect(prompts[0].user).toContain('память судьи plan');
|
||||
});
|
||||
it('SP2c-2: roundMemoryImpl (spec) → stage spec в промпте судьи', async () => {
|
||||
const prompts = [];
|
||||
const specEv = { tool_name: 'Write', tool_input: { file_path: 'docs/superpowers/specs/x-design.md', content: '## R {#r}\nтекст' } };
|
||||
await runJudgeGate(specEv, {
|
||||
judgeActiveImpl: () => true, apiKey: 'K', mentorApproved: () => true,
|
||||
transport: async (p) => { prompts.push(p); return okText; },
|
||||
roundMemoryImpl: ({ stage }) => ({ objections: [`память судьи ${stage}`] }),
|
||||
});
|
||||
expect(prompts[0].user).toContain('память судьи spec');
|
||||
});
|
||||
it('активен, но не план (Bash) → wired:false, транспорт не зовётся ($0)', async () => {
|
||||
let calls = 0;
|
||||
const deps = { judgeActiveImpl: () => true, apiKey: 'K', transport: async () => { calls++; return okText; } };
|
||||
|
||||
Reference in New Issue
Block a user