Files
brain/tools/question-slots.mjs
T

37 lines
2.2 KiB
JavaScript

#!/usr/bin/env node
/**
* question-slots — трекинг заданных вопросов разговорной фазы как СЛОТОВ с id
* (условие R7 spec 2026-06-05): механическая проверка «нет повисших вопросов»
* работает только если вопрос помечается слотом (задан/отвечен), а не вольным
* текстом. Чистый модуль, иммутабельные операции ($0, без LLM).
*/
function isNonEmptyString(v) { return typeof v === 'string' && v.trim().length > 0; }
/** Добавить заданный вопрос (новый слот answered:false). id — непустой уникальный. */
export function addQuestion(slots, { id, text } = {}) {
const list = Array.isArray(slots) ? slots : [];
if (!isNonEmptyString(id)) throw new Error('question id: непустая строка обязательна');
if (!isNonEmptyString(text)) throw new Error('question text: непустая строка обязательна');
if (list.some((s) => s.id === id)) throw new Error(`question id дублируется: ${id}`);
return [...list, { id, text, answered: false, answer: null }];
}
/** Пометить вопрос отвеченным (непустой ответ). Неизвестный id → ошибка. */
export function answerQuestion(slots, id, answer) {
const list = Array.isArray(slots) ? slots : [];
if (!list.some((s) => s.id === id)) throw new Error(`неизвестный question id: ${id}`);
if (!isNonEmptyString(answer)) throw new Error('answer: непустая строка обязательна');
return list.map((s) => (s.id === id ? { ...s, answered: true, answer } : s));
}
/** Повисшие вопросы — заданные, но не отвеченные. */
export function hangingQuestions(slots) {
return (Array.isArray(slots) ? slots : []).filter((s) => !s.answered);
}
/** Ни один заданный вопрос не повис (пустой список → true, вопросов нет). */
export function allAnswered(slots) {
return hangingQuestions(slots).length === 0;
}