Files
brain/tools/secretary-protocol.test.mjs
T
Дмитрий ab8abe2c87 feat(secretary): История+многоходовый провенанс в хуке, нарезка сырья по ходам, обезвреживание маркеров, все Шаги
- stampProvenance ведёт История-таймлайн (in/out) и многоходовый провенанс при смене зачёркивания строки
- splitRawIntoTurns/prepareTurnFiles: нарезка raw на <дело>/ходы/turn-N.log; Шаги ссылаются на файл хода
- buildStepsFromRaw + обработчик off: Шаг на КАЖДЫЙ ход (без пропусков выкл-ходов)
- neutralizeMarkers в buildRawRecord: защита от самозагрязнения лога копиями маркеров
- полная форма протокола (9 категорий) + дело создание-секретаря приведено к виду; набор секретаря 56/56

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:24:20 +03:00

89 lines
5.0 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { renderProtocol, EMPTY_PROTOCOL } from './secretary-protocol.mjs';
describe('EMPTY_PROTOCOL', () => {
it('пустой протокол со всеми 9 разделами', () => {
expect(EMPTY_PROTOCOL()).toEqual({
subject: '', status: 'открыто',
decisions: [], alternatives: [], consequences: [],
will: [], open: [], doneNext: [], history: [], steps: [],
});
});
});
describe('renderProtocol — 9 категорий + шаги', () => {
const proto = {
subject: 'фоновый секретарь', status: 'открыто',
decisions: [{ text: 'D', why: 'w', turns: [7], session: '69992620-x' }],
alternatives: [{ text: 'ALT', turns: [8], session: '69992620-x' }],
consequences: [{ text: 'CONS', turns: [9], session: '69992620-x' }],
will: [{ text: 'W', turns: [10], session: '69992620-x' }],
open: [{ text: 'Q', turns: [11], session: '69992620-x' }],
doneNext: [{ text: 'N', done: false, turns: [12], session: '69992620-x' }],
history: [],
};
it('шапка «Дело» со статусом/хозяином/целью (по opts)', () => {
const md = renderProtocol(proto, { work: 'создание-секретаря', date: '2026-06-22 11:00' });
expect(md).toContain('**Дело:** создание-секретаря');
expect(md).toContain('**Статус:** открыто');
expect(md).toContain('**Хозяин:** владелец');
expect(md).toContain('**Цель:** фоновый секретарь');
});
it('разделы Альтернативы и Последствия / цена', () => {
const md = renderProtocol(proto);
expect(md).toContain('## Альтернативы');
expect(md).toContain('- ALT');
expect(md).toContain('## Последствия / цена');
expect(md).toContain('- CONS');
});
it('провенанс [→N] без метки файла @ (имя файла — только в Шагах)', () => {
const md = renderProtocol(proto);
expect(md).toContain('- D — w [→7]');
expect(md).not.toContain('@69992620');
expect(md).toContain('## Твоя воля / запреты');
expect(md).toContain('## Открытые вопросы');
});
it('история: тайм-линия toggle (внёс →, вынес ←, вернул →, снова вынес ←)', () => {
const md = renderProtocol({
subject: '', status: 'открыто', steps: [],
decisions: [], alternatives: [], consequences: [], will: [], open: [], doneNext: [],
history: [{ text: 'пункт X', events: [{ turn: 41, dir: 'in' }, { turn: 43, dir: 'out' }, { turn: 55, dir: 'in' }, { turn: 70, dir: 'out' }] }],
});
expect(md).toContain('~~пункт X~~ [→41] [←43] [→55] [←70]');
});
it('провенанс с несколькими ходами: [→33], [50]', () => {
const md = renderProtocol({
subject: '', status: 'открыто', steps: [], alternatives: [], consequences: [], will: [], open: [], doneNext: [], history: [],
decisions: [{ text: 'Y', why: null, turns: [33, 50] }],
});
expect(md).toContain('- Y [→33], [50]');
});
it('зачёркивание во всех корзинах', () => {
const md = renderProtocol({
subject: '', status: 'открыто', history: [],
decisions: [{ text: 'D', struck: true }], alternatives: [{ text: 'A', struck: true }],
consequences: [{ text: 'C', struck: true }], will: [{ text: 'W', struck: true }],
open: [{ text: 'Q', struck: true }], doneNext: [{ text: 'N', struck: true, done: false }],
});
for (const t of ['~~D~~', '~~A~~', '~~C~~', '~~W~~', '~~Q~~', '~~N~~']) expect(md).toContain(t);
});
it('раздел Шаги: ссылка на отдельный файл хода (s.file) вместо общего лога', () => {
const md = renderProtocol({
subject: '', status: 'открыто', history: [],
decisions: [], alternatives: [], consequences: [], will: [], open: [], doneNext: [],
steps: [{ turn: 1, session: 'sess', file: 'ходы/turn-1.log', text: 'Ход 1 — я: x · ты: y · делал: —' }],
});
expect(md).toContain('Ход 1 — я: x · ты: y · делал: — · ходы/turn-1.log');
expect(md).not.toContain('· sess.log');
});
it('раздел Шаги (Слой 1): строка на ход + название файла полного хода в конце строки', () => {
const md = renderProtocol({
subject: '', status: 'открыто', history: [],
decisions: [], alternatives: [], consequences: [], will: [], open: [], doneNext: [],
steps: [{ turn: 1, session: '69992620-x', text: 'Ход 1 — я: про оглавление · ты: тема+время · делал: читал хук' }],
});
expect(md).toContain('## Шаги (Слой 1)');
expect(md).toContain('Ход 1 — я: про оглавление · ты: тема+время · делал: читал хук · 69992620-x.log');
});
});