Files
brain/tools/secretary-apply.mjs
T

80 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// tools/secretary-apply.mjs
// Детерминированное применение результатов спана к реестру: садовник + новые ветки + кандидаты.
import { one } from './secretary-armor.mjs';
import { applyTend, makeHeir } from './secretary-gardener.mjs';
import { collapseProtocol } from './secretary-reconcile.mjs';
const norm = (s) => String(s || '').trim().toLowerCase().replace(/\s+/g, ' ');
/** Зажим тяжести: догадка не может быть блокером → важное (правило тяжести, находка №13). */
function clampSeverity(опора, тяжесть) {
return (one(опора) === 'догадка' && тяжесть === 'блокер') ? 'важное' : (тяжесть || 'важное');
}
/** Разложить результаты спана по реестру. Вход НЕ мутируется (работаем на копии). */
export function applyResults(proto, turn, d11, d12, d13, dG) {
const p = JSON.parse(JSON.stringify(proto));
p.hidden = p.hidden || []; p.nextSvId = p.nextSvId || 1; p.candidates = p.candidates || [];
// садовник (тенд по id; close без proof → carry — внутри applyTend)
applyTend(p, (dG && dG.tend) || [], turn);
// новые ветки из диагностики
for (const n of (d11 && d11.new) || []) {
p.hidden.push({ id: `СВ-${p.nextSvId++}`, lens: n.lens || 'Л?', status: 'открыт', text: String(n.text || ''), опора: one(n.опора), ref: n.ref || '', тяжесть: clampSeverity(n.опора, n.тяжесть), born: turn, lastTouch: turn, lineage: [] });
}
// ловец → долговечная ветка lens=брош
for (const dr of (d12 && d12.dropped) || []) {
p.hidden.push({ id: `СВ-${p.nextSvId++}`, lens: 'брош', status: 'открыт', text: String(dr.text || ''), опора: one(dr.опора), ref: dr.цитата || '', тяжесть: 'важное', born: turn, lastTouch: turn, lineage: [] });
}
// кандидаты — НАКАПЛИВАЮТСЯ (ход рождения, без дублей по branch), помечены слабыми (доживают до разбора)
const seen = new Set(p.candidates.map((c) => norm(c.branch)));
for (const f of (d13 && d13.forks) || []) {
if (seen.has(norm(f.branch))) continue;
seen.add(norm(f.branch));
p.candidates.push({ branch: f.branch, trigger: f.trigger, why: f.why, опора: one(f.опора), релевантность: f.релевантность || 'low', born: turn });
}
return p;
}
/** ЭТАП 1 (fluffy v2): урожай — новые ветки (диагност+ловец) + рождение/заточка/полнота кандидатов (брейншторм).
* Садовника НЕ применяет (это applyGardener отдельно, на свежей грядке). Вход НЕ мутируется. */
export function applyHarvest(proto, turn, d11, d12, d13) {
const p = JSON.parse(JSON.stringify(proto));
p.hidden = p.hidden || []; p.candidates = p.candidates || []; p.nextSvId = p.nextSvId || 1; p.nextKdId = p.nextKdId || 1;
// новые ветки (диагностика) — с title/тема/важность/history
for (const n of (d11 && d11.new) || []) p.hidden.push({ id: `СВ-${p.nextSvId++}`, lens: n.lens || 'Л?', status: 'открыт',
title: n.title || '', тема: n.тема || '', важность: n.важность || 'medium', text: String(n.text || ''),
опора: one(n.опора), ref: n.ref || '', тяжесть: clampSeverity(n.опора, n.тяжесть), born: turn, lastTouch: turn, lineage: [],
history: [{ turn, ev: 'рождён', why: `диагност ${n.lens || ''}`, detail: n.ref || '' }] });
// ловец → ветка брош
for (const dr of (d12 && d12.dropped) || []) p.hidden.push({ id: `СВ-${p.nextSvId++}`, lens: 'брош', status: 'открыт',
title: dr.title || '', тема: dr.тема || '', важность: dr.важность || 'medium', text: String(dr.text || ''),
опора: one(dr.опора), ref: dr.цитата || '', тяжесть: 'важное', born: turn, lastTouch: turn, lineage: [],
history: [{ turn, ev: 'рождён', why: 'ловец (брошенная ветка)', detail: dr.цитата || '' }] });
// брейншторм точит СВОИХ кандидатов (мутация → наследник)
for (const m of (d13 && d13.mutate) || []) {
const it = p.candidates.find((x) => x.id === m.id);
if (!it || (it.status && it.status !== 'жив')) continue;
it.history = it.history || []; it.history.push({ turn, ev: 'мутировал', why: m.why || '', detail: m.newText || '' });
it.status = 'мутировал'; it.endedTurn = turn; it.why = m.why;
makeHeir(it, turn, m.newText, m.newTitle, 'cand', p, m.why, 'рождён заточкой');
}
// брейншторм рождает новых (дедуп по branch) — с id/title/тема/важность/history
const seen = new Set(p.candidates.map((c) => norm(c.branch)));
for (const f of (d13 && d13.forks) || []) { if (seen.has(norm(f.branch))) continue; seen.add(norm(f.branch));
p.candidates.push({ id: `КД-${p.nextKdId++}`, branch: f.branch, title: f.title || '', тема: f.тема || '', важность: f.важность || 'low',
trigger: f.trigger, why: f.why, опора: one(f.опора), релевантность: f.релевантность || 'low', born: turn, status: 'жив',
history: [{ turn, ev: 'рождён', why: 'брейншторм', detail: f.trigger || '' }] }); }
// критик полноты (зияющий пробел ЦЕЛОГО пласта) — дедуп по теме
const themesHave = new Set([...p.hidden, ...p.candidates].map((x) => norm(x.тема)).filter(Boolean));
for (const g of (d13 && d13.gaps) || []) { const th = norm(g.тема); if (th && themesHave.has(th)) continue; themesHave.add(th);
p.candidates.push({ id: `КД-${p.nextKdId++}`, branch: g.why || g.title || '', title: g.title || '', тема: g.тема || 'полнота',
важность: 'high', gap: true, why: g.why, опора: 'догадка', релевантность: 'high', born: turn, status: 'жив',
history: [{ turn, ev: 'рождён', why: 'критик полноты (зияющий пробел пласта)', detail: g.title || '' }] }); }
return collapseProtocol(p);
}