#!/usr/bin/env node /** * arbitration-card — чистый сборщик карточки арбитража (спека §6). Дословные цитаты обеих * сторон + нейтральная строка «что меняет выбор» на каждый вариант + аффорданс «объясни * подробнее». НЕ перефразирует: objection/position проходят как есть (только String-coerce). * Решение владельца пишется НЕ здесь, а через escape-grant/waivers (П3). */ export const ARBITRATION_OPTIONS = ['hold', 'accept', 'own']; const verbatim = (s) => (typeof s === 'string' ? s : ''); export function buildArbitrationCard({ side, level, round, objectionVerbatim, controllerPositionVerbatim, sealAction = null } = {}) { const who = side === 'judge' ? 'судьи' : 'наставника'; // SP3-c: sealAction = `owner-seal:<хеш тела>` (тот же, что считает sealTurnProd: спека → // judgedHashOf(sealableArtifact), план → planId(steps)). Несём его в карточку + в честный // hold-текст, чтобы владельцу было ОТКУДА взять точную escape-строку (ownerseal-wiring-bug). const seal = typeof sealAction === 'string' && sealAction ? sealAction : null; const holdText = seal ? `Опечатать версию контроллера как есть — подпиши escape-опцией: FLOOR-ESCAPE: ${seal} (замечание ${who} перевешено владельцем).` : `Ворота пропустят версию контроллера — замечание ${who} отведено тобой (escape-грант).`; return { side, level, round, sealAction: seal, title: `Арбитраж (${level}): тупик спора с ${side === 'judge' ? 'судьёй' : 'наставником'} после ${round} кругов`, objection: verbatim(objectionVerbatim), position: verbatim(controllerPositionVerbatim), options: [ { key: 'hold', label: 'Держусь позиции контроллера', whatChanges: holdText }, { key: 'accept', label: 'Согласиться с замечанием', whatChanges: `Контроллер переделает по замечанию ${who}; счётчик круга сбрасывается.` }, { key: 'own', label: 'Своё решение', whatChanges: 'Ты вписываешь своё — оно становится обязательным для контроллера.' }, ], explainAffordance: true, }; }