diff --git a/editorial/production/README.md b/editorial/production/README.md index 03edac7..69d2703 100644 --- a/editorial/production/README.md +++ b/editorial/production/README.md @@ -1,6 +1,6 @@ # Производство редакционных партий -На 31 июля 2026 года строгий аудит проходит 271 из 358 созданных материалов. Остальные 87 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. +На 31 июля 2026 года строгий аудит проходит 274 из 358 созданных материалов. Остальные 84 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. ## Одна партия diff --git a/editorial/reviews/2025-08-draft.md b/editorial/reviews/2025-08-draft.md new file mode 100644 index 0000000..469990f --- /dev/null +++ b/editorial/reviews/2025-08-draft.md @@ -0,0 +1,43 @@ +# P90 · Август 2025 · Исследование UX внутреннего инструмента + +## Проход 1 — факты, техника и источники + +- Проверен `web/scripts/upgrade-2025-08.mjs`: модель содержит только frozen fixed synthetic literals. В ней нет чтения/записи файлов, сети, clock, telemetry, production, PII, recording, Git, CI или real API; `productionEffect` в положительной и отрицательных ветках — `not-attempted`. +- Источники — первичные, публичные HTTPS-руководства GOV.UK. У consent-страницы видны публикация 24 May 2016 и update 5 November 2018; руководства по заметкам и планированию также опубликованы до исторической границы 31 August 2025. Изменяемые оригиналы закреплены точными immutable archive captures 2025-07-02; в source list у каждого показаны version/date, claim и boundary. Они не использованы как универсальная legal policy и не выданы за доказательство UX-эффекта. +- Механика проверена по negative paths: withdrawn consent, recording вне notes-only boundary, leading prompt и decision без ссылки на observation останавливают synthetic hand-off. Положительная ветка разрешает только `observe-same-fixed-scenario-only`, а не UI release. + +## Проход 2 — редактура, голос и плотность + +- Прочитаны все три тела без списка источников. Практика, механизм и полевой журнал начинают с наблюдаемой проблемы и цены ошибки в первых двух абзацах; далее используют последовательность «симптом → причина → проверка → действие». Большой общий блок удалён: у каждого текста собственные определения, executable negative example, sequence и next step. Проверка тел не нашла совпадающих предложений длиной от 90 знаков; пересечение словаря попарно 0.257–0.264. +- Термины раскрыты при первом появлении: neutral task scenario, informed consent, evidence coding, consent boundary, observation, code, theme, decision log и follow-up. Нет вымышленных сотрудников, интервью, денег, метрик, записей или утверждений о production-результате. +- Тон соответствует M8: решение отделено от evidence, названа цена повторной разработки и спорного UI-изменения, а финал каждой статьи оставляет ограничение и следующий проверяемый шаг. Прогоны audit ниже фиксируют фактический объём в диапазоне 5 000–15 000 знаков. + +## Проход 3 — визуал, fixture и выпускной контроль + +- `node --check`, `--verify-fixture` (15/15), `npm run audit:draft -- scripts/upgrade-2025-08.mjs` (7 053 / 7 536 / 7 836 знаков), XML-проверка и SVG safety scan пройдены. +- Все три SVG имеют title/desc, содержательный `alt` и подпись в статье. Sharp render и ручной просмотр на ширине 375 px пройдены: evidence map — путь consent → scenario → observation → decision; matrix — условия STOP; feedback loop — isolated follow-up без автоматического релиза. +- Пакет намеренно не подключён к registry, README, очереди, базе статей или Git. Для публикации нужен отдельный независимый редакторский приёмочный проход. + +## Независимый выпускной проход — 31.07.2026 + +Три статьи прочитаны целиком вне self-review. Их роли не совпадают: practice строит нейтральный сценарий сессии, mechanism проверяет traceability между evidence и decision, field ведёт короткий review journal. Автоматическая попарная проверка основного текста дала 0 одинаковых абзацев от 160 знаков и 0 общих 12-словных фрагментов. + +Архивные GOV.UK captures от 02.07.2025 запрошены повторно. Consent capture подтверждает informed consent и withdrawal; notes/recording capture — consent до заметок и записей и использование только в согласованных границах; planning capture — порядок «problems, assumptions, what needs to be learned for a next decision». Это ровно граница утверждений в статьях, без legal или outcome claim. + +Три видимых code example исполнены через публичные exports: leading prompt возвращает `neutral-task-scenario-required`; unlinked decision и withdrawn consent возвращают `stop-and-repair-boundary`. SVG повторно отрендерены Sharp на 375 px: evidence map, matrix и feedback loop читаемы, safety scan чист. + +~~~text +node --check scripts/upgrade-2025-08.mjs +PASS + +node scripts/upgrade-2025-08.mjs --verify-fixture +PASS fixture: 15/15 assertions + +npm run audit:draft -- scripts/upgrade-2025-08.mjs +PASS: 7053 / 7536 / 7836 body chars + +npm run audit:articles -- <3 August slugs> +PASS: each article has 1 table, 1 code example and 1 figure +~~~ + +После приёмки overlay подключён в `editorial-revisions.mjs` после July 2025. Его fixed synthetic boundary, `not-attempted` production effect и запрет на реальный UI release остаются неизменными. diff --git a/web/data/editorial-revisions.mjs b/web/data/editorial-revisions.mjs index afe33a6..a2f675c 100644 --- a/web/data/editorial-revisions.mjs +++ b/web/data/editorial-revisions.mjs @@ -86,6 +86,7 @@ import { revisions as april2025Revisions } from '../scripts/upgrade-2025-04.mjs' import { revisions as may2025Revisions } from '../scripts/upgrade-2025-05.mjs'; import { revisions as june2025Revisions } from '../scripts/upgrade-2025-06.mjs'; import { revisions as july2025Revisions } from '../scripts/upgrade-2025-07.mjs'; +import { revisions as august2025Revisions } from '../scripts/upgrade-2025-08.mjs'; // This layer replaces archived source entries without losing their stable slug and date. export const editorialRevisions = [ @@ -177,4 +178,5 @@ export const editorialRevisions = [ ...may2025Revisions, ...june2025Revisions, ...july2025Revisions, + ...august2025Revisions, ]; diff --git a/web/public/assets/editorial/2025/tool-ux-research-2025-evidence-map.svg b/web/public/assets/editorial/2025/tool-ux-research-2025-evidence-map.svg new file mode 100644 index 0000000..3e70e79 --- /dev/null +++ b/web/public/assets/editorial/2025/tool-ux-research-2025-evidence-map.svg @@ -0,0 +1,10 @@ + diff --git a/web/public/assets/editorial/2025/tool-ux-research-2025-observation-decision-matrix.svg b/web/public/assets/editorial/2025/tool-ux-research-2025-observation-decision-matrix.svg new file mode 100644 index 0000000..aa2364a --- /dev/null +++ b/web/public/assets/editorial/2025/tool-ux-research-2025-observation-decision-matrix.svg @@ -0,0 +1,8 @@ + diff --git a/web/public/assets/editorial/2025/tool-ux-research-2025-research-feedback-loop.svg b/web/public/assets/editorial/2025/tool-ux-research-2025-research-feedback-loop.svg new file mode 100644 index 0000000..7b2354b --- /dev/null +++ b/web/public/assets/editorial/2025/tool-ux-research-2025-research-feedback-loop.svg @@ -0,0 +1,12 @@ + diff --git a/web/scripts/upgrade-2025-08.mjs b/web/scripts/upgrade-2025-08.mjs new file mode 100644 index 0000000..d4b5dc3 --- /dev/null +++ b/web/scripts/upgrade-2025-08.mjs @@ -0,0 +1,212 @@ +function escapeHtml(value) { + return String(value).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", '''); +} + +const p = (text) => '
' + text + '
'; +const h2 = (text) => '' + escapeHtml(text) + '';
+const ol = (items) => '| ' + item + ' | ').join('') + '
|---|
| ' + item + ' | ').join('') + '
owner-unclear говорит только, что в одном маршруте следующий owner не найден. Рядом обязательна uncertainty: неизвестна частота этого вопроса и причина невидимости поля. Такая пара не позволяет превратить единичную фразу в утверждение о всех коллегах.'),
+ h2('Воспроизводимый маршрут практики'),
+ code("import { createFixedResearchInput, inspectToolUxResearch } from './upgrade-2025-08.mjs';\n\nconst report = inspectToolUxResearch(\n createFixedResearchInput('fixed-leading-prompt-v1'),\n);\nconsole.log(report.reasons);\n// Expected: ['neutral-task-scenario-required'].\n// The input is fixed in-memory synthetic data; no session or UI is started."),
+ p('Этот negative path намеренно подменяет task prompt ведущим вопросом о большой кнопке. Инспектор останавливает hand-off до coding и до candidate change. Он не определяет качество реальной беседы по одному шаблону текста: это ограниченный executable пример того, что сценарий задачи и просьба подтвердить решение автора — разные входы.'),
+ h2('Шесть действий ведущего сессии'),
+ ol(['Записать неизвестное решение. Не «добавить кнопку», а «понять, что мешает назвать следующий шаг».', 'Показать consent card до сценария. Уточнить цель, notes-only boundary, отсутствие записи и путь отзыва.', 'Дать задачу без интерфейсной подсказки. Попросить разобраться со status и owner, а не оценить нарисованный блок.', 'Записать маршрут буквально. Порядок прочтения, остановка, вопрос; без ярлыка «запутался».', 'Добавить code и uncertainty. Метка связывает заметку с дальнейшим разбором, а неизвестное не даёт раздуть её силу.', 'Передать только candidate follow-up. Новая проверка повторяет ту же задачу; выпуск UI не следует из одной заметки.']),
+ p(SYNTHETIC_BOUNDARY),
+ h2('Consent card — это техническая граница материала'),
+ p('В руководстве GOV.UK informed consent включает цель, собираемые данные, способ использования, наблюдение или запись, добровольность и возможность отзыва. Для инженера это не формальность в конце календарного приглашения. Карточка определяет, какие поля имеет право получить протокол. Если согласованы только обезличенные notes, исследователь не добавляет запись «на всякий случай», не пересылает экран и не оставляет имя в decision log.'),
+ p('В модели consent status withdrawn приводит к stop. Второй negative path разрешает screen recording при notes-only boundary и тоже останавливает работу. Такой код не исполняет удаление реальных материалов; у него нет файлов и реальных записей. Он делает видимым правило, которое легко потерять в ручной таблице: evidence вне согласованного набора не превращается в «полезный дополнительный контекст».'),
+ h2('Заметка сначала, тема потом'),
+ p('Хорошая observation record скучна: «прочитан status; курсор остановился у owner; сформулирован вопрос о следующем ответственном». В ней нет фразы «пользователь растерялся» и нет диагноза команды. Code owner-unclear связывает запись с темой, но его значение ограничено: он указывает, что owner не найден в этом маршруте, а не почему это случилось и не сколько людей столкнутся с тем же.'),
+ p('Неопределённость записывают рядом с кодом. Это экономит спор: дизайнер не обязан доказывать, что текст плох, а инженер не обязан угадывать, что имелось в виду. Следующий вопрос становится конкретным: показать ли owner до отправки, пояснить ли status, изменить ли порядок полей или сначала собрать ещё один observation. Когда evidence не различает варианты, решение — не «самый убедительный спикер», а новый сценарий.'),
+ h2('Ограничение и следующий шаг'),
+ p('Один сценарий не является исследованием всей организации. Он не даёт метрик, денег, реальных сотрудников, записей, репрезентативности или причины задержки. GOV.UK guidance поддерживает идею явного согласия и планирования вопросов, но не переносит свои policy на другой контекст. Следующий шаг: возьмите одну внутреннюю задачу, вычеркните из prompt название будущей кнопки и добавьте к каждой заметке одну строку uncertainty. Ожидаемый результат — decision log содержит гипотезу, которую можно опровергнуть на следующем проходе.'),
+ h2('Историческая граница августа 2025'),
+ p('Использованы три первичных руководства GOV.UK, опубликованные до 31 августа 2025; для каждого в источниках видна датированная версия страницы. Они применены только к consent, заметкам и постановке исследовательского вопроса. Fixed data model, codebook, stop reasons и candidate change — инженерская конструкция автора. Она не доказывает, что гипотеза верна, и не подменяет policy, legal review или живое исследование.'),
+]);
+
+const mechanism = revision({ slug: 'editorial-2025-08-mechanism-tool-ux-research', title: 'Исследование UX внутреннего инструмента: от заметки к решению без удобной причинности', categories: ['Инструменты', 'UX'], cover: '/assets/editorial/2025/tool-ux-research-2025-observation-decision-matrix.svg', excerpt: 'Механика evidence coding: отделить факт от темы, тему от candidate change и остановить вывод, если consent, сценарий или ссылка на наблюдение неполны.', readingMinutes: 13 }, [
+ p('После внутреннего интервью в заметках остаётся фраза «коллеге неудобно», а в задаче уже появляется выбранный редизайн. Наблюдаемый симптом — решение не содержит исходного действия, consent boundary или неизвестного; его нельзя перепроверить. Цена ошибки — невидимая подмена: команда называет правку UX-исследованием и теряет время, когда следующий сценарий снова не проходит.'),
+ p('Причина — четыре разных объекта сливаются в одну цитату. Наблюдение говорит, что произошло; code группирует похожие записи; theme формулирует проверяемую область; decision выбирает следующий шаг. Проверка — требовать ссылки decision на конкретные observation ids и явный статус not-established. Действие — остановить карточку, если связь, consent или neutral scenario отсутствуют.'),
+ h2('Четыре уровня evidence'),
+ p('Observation — это самый узкий уровень. В fixed модели он сообщает последовательность на карточке заявки и вопрос об owner. Codebook даёт этому фрагменту метку owner-unclear. Theme next-step-visibility объединяет owner и status только как рабочий вопрос: видит ли исполнитель, что делать после текущего состояния. Candidate change добавляет компактный блок следующего шага, но не заявляет, что он «улучшит UX». Так каждая ступень сохраняет собственную силу утверждения.'),
+ figure('/assets/editorial/2025/tool-ux-research-2025-observation-decision-matrix.svg', 'Матрица observation-to-decision: две fixed заметки с кодами ведут к теме next-step visibility; candidate change допускается только с ссылками на обе заметки и с claim not-established. Отсутствие consent, нейтральности или ссылки даёт STOP.', 'Матрица намеренно не содержит процент удобства. Она показывает, какое доказательство разрешает следующий synthetic follow-up, а не выпуск интерфейса.'),
+ table('Граница силы утверждения', ['Уровень', 'Содержит', 'Проверка', 'Запрещённый вывод'], [['Observation', 'видимое действие или вопрос', 'есть scenario id и uncertainty', '«все не понимают»'], ['Code', 'короткая метка записи', 'code существует в codebook', '«код объяснил причину»'], ['Theme', 'объединённый вопрос', 'можно назвать альтернативы', '«нужна именно эта кнопка»'], ['Decision', 'candidate change и follow-up', 'есть ссылки на observations', '«изменение уже помогло»']]),
+ h2('Словарь механизма: что разрешает каждая запись'),
+ p('Consent boundary отвечает на вопрос «какой evidence разрешён». Observation отвечает «что произошло в заданном сценарии». Code — стабильное имя наблюдаемой детали; theme — вопрос, который объединяет несколько coded observations. Decision log — карточка следующего действия с ссылками на evidence. Эти уровни нельзя переставлять: theme без observations становится мнением, а decision без theme — списком любимых UI-идей.'),
+ p('В fixed наборе разрешены de-identified notes, а screen recording запрещён. Это не готовая политика хранения. Настоящая команда отдельно определяет владельца, срок, доступ и процесс отзыва. Модель проверяет только собственный контракт: один тип evidence, confirmed consent, существующие codes и ссылочная целостность decision. Если хоть одна граница нарушена, итог — stop-before-change.'),
+ h2('Воспроизводимый разбор связи evidence и решения'),
+ code("import { createFixedResearchInput, inspectToolUxResearch, prepareSyntheticFollowUp } from './upgrade-2025-08.mjs';\n\nconst input = createFixedResearchInput('fixed-unlinked-decision-v1');\nconst report = inspectToolUxResearch(input);\nconsole.log(prepareSyntheticFollowUp(report));\n// Expected: stop-and-repair-boundary with an untraceable decision.\n// No files, recordings, APIs or production data participate."),
+ p('В этой ветке candidate change выглядит правдоподобно, но ссылается на отсутствующий observation id. Поэтому нельзя проверить, что решение выросло из работы по задаче, а не из памяти workshop. Инспектор не предлагает другую кнопку и не ремонтирует ссылку сам. Он возвращает причину, после которой владелец должен либо восстановить evidence, либо сформулировать новый сценарий.'),
+ h2('Проверка механики по порядку'),
+ ol(['Проверить consent status. Withdrawn означает stop, даже если заметка кажется полезной.', 'Проверить тип evidence. Notes-only boundary не расширяется записью ради более удобного анализа.', 'Проверить prompt. Scenario описывает действие и успех, а не ответ, который хочет услышать автор.', 'Проверить coding. У каждой observation есть допустимый code и uncertainty, иначе theme не имеет опоры.', 'Проверить ссылки decision. Каждый cited id должен существовать в том же наборе наблюдений.', 'Ограничить hand-off. Положительный результат разрешает лишь isolated follow-up на той же задаче.']),
+ p('Пакет хранит только fixed synthetic JavaScript literals в памяти. Он не читает, не пишет и не отправляет данные; `productionEffect` всегда `not-attempted`. Поэтому пример воспроизводит проверку контракта, но не проверяет людей, реальный интерфейс или effect candidate change.'),
+ h2('Почему neutral scenario важнее красивого interview guide'),
+ p('Ведущий вопрос сокращает дорогу к согласию, но убирает информацию. Формулировка «вам ведь не хватает большой кнопки?» уже содержит объект, размер и оценку. Нейтральный сценарий говорит только о задаче: найти состояние заявки и назвать следующий owner. Он позволяет наблюдать обход, термин, отсутствие статуса или другой путь. Если исследователь хочет проверить конкретную кнопку, это отдельный usability test после того, как проблема описана без решения.'),
+ p('Fixture показывает этот разрыв буквально. Когда neutralPrompt заменён на forbidden prompt, инспектор возвращает neutral-task-scenario-required. В реальной работе нельзя свести качество вопроса к регулярному выражению. Но эта fixed проверка полезна как отрицательный пример: модель обязана различать сценарий задачи и аргумент автора, иначе удачный ответ будет встроен в входные данные.'),
+ h2('Decision log должен говорить, чего он не знает'),
+ p('Полезный decision log не прячет неуверенность за словами «инсайт» и «паттерн». Он перечисляет observation ids, тему, candidate change, владельца следующего шага и границу претензии. В данном пакете claim начинается с not-established: ни удобство, ни скорость, ни снижение нагрузки не были измерены. Это не ослабляет документ. Наоборот, reviewer видит, что обсуждать: достаточно ли evidence для следующего сценария, а не для массового запуска.'),
+ p('Вторая защита — одинаковый сценарий follow-up. Если baseline просит проверить статус, а после правки просит «оценить новый блок», сравниваются разные действия и контекст. Fixed record требует тот же objective, starting state и neutral prompt. Это не экспериментальный дизайн и не случайное распределение. Это минимальная защита от очень частой ошибки: принять новую формулировку вопроса за эффект новой поверхности.'),
+ h2('Отрицательные ветки — часть исследования'),
+ p('Consent withdrawn останавливает работу до coding. Recording outside notes-only boundary останавливает её даже при полезной заметке. Unlinked decision останавливает candidate change, потому что reviewer не может восстановить его происхождение. Эти стопы не означают, что реальный человек или организация нарушили правило: все values synthetic. Они показывают, что безопасная система не должна лечить пробел в evidence догадкой или удачной цитатой.'),
+ h2('Ограничение и следующий шаг'),
+ p('Codebook не является нейтральной машиной истины: выбор меток влияет на то, что команда замечает. Руководства GOV.UK помогают описать consent и план, но не дают универсальный список кодов и не переносят согласие одной организации на другую. Следующий шаг: возьмите один старый research note, отдельно выпишите факт, code, theme и decision. Если decision нельзя связать с конкретной заметкой, не спорьте о формулировке кнопки — сначала заведите новый нейтральный сценарий.'),
+ h2('Историческая граница августа 2025'),
+ p('Источники относятся к доступным до 31 августа 2025 руководствам GOV.UK; их даты и границы приведены в списке источников. Из них взяты только требования к осмысленному согласию, работе с заметками и планированию вопросов. Разделение evidence levels, exact JS literals и stop rules не являются предписанием GOV.UK и не доказывают реальный outcome.'),
+]);
+
+const field = revision({ slug: 'editorial-2025-08-field-tool-ux-research', title: 'Полевой журнал UX внутреннего инструмента: short evidence before UI change', categories: ['Инструменты', 'UX'], cover: '/assets/editorial/2025/tool-ux-research-2025-research-feedback-loop.svg', excerpt: 'Полевой формат: собрать consent boundary, observation record, uncertainty, decision log и follow-up так, чтобы изменение интерфейса можно было честно проверить на следующей задаче.', readingMinutes: 13 }, [
+ p('Команда получает просьбу «починить форму заявки» и сразу собирает board из цитат. Наблюдаемый симптом — в карточке решения нет ни задачи, ни границы согласия, ни того, что именно видел коллега. Цена ошибки — новый интерфейс становится дорогим спором: никто не может показать, какая проблема была проверена, а следующая обратная связь снова сводится к вкусу.'),
+ p('Причина — материал исследования живёт в заметках, а изменение — в отдельной задаче. Проверка — вести короткий журнал, в котором consent card, scenario, observation, code, uncertainty, decision и follow-up соединены идентификаторами. Действие — выпускать только hypothesis for follow-up; если журнал не восстанавливает путь к заметке, интерфейс не меняют под видом evidence.'),
+ h2('Минимальный журнал помещается в один review'),
+ p('Журнал не заменяет репозиторий записей и не собирает персональные данные. Ему достаточно семи полей: purpose, consent boundary, scenario, observation, code, uncertainty, decision. Для fixed примера purpose — найти следующий шаг в заявке; consent разрешает только de-identified note; scenario не называет будущий блок; decision предлагает проверить компактное пояснение status и owner. Каждая строка указывает, что она знает, а что оставляет неизвестным.'),
+ figure('/assets/editorial/2025/tool-ux-research-2025-research-feedback-loop.svg', 'Замкнутый цикл fixed synthetic UX-исследования: consent card ограничивает запись, нейтральный task scenario порождает observation record с code и uncertainty, decision log формирует candidate change, затем тот же сценарий создаёт новый isolated record или STOP. Цикл не содержит автоматического релиза.', 'Цикл отделяет исследовательскую обратную связь от delivery: решение порождает следующий проверяемый материал, но не объявляет результат интерфейса заранее.'),
+ table('Fixed synthetic decision log', ['Поле', 'Значение', 'Вопрос reviewer'], [['Consent boundary', 'de-identified note only; no recording', 'какой материал вообще допустим?'], ['Scenario', 'найти status и next owner', 'нет ли подсказки решения?'], ['Observation ids', 'owner-v1, status-v1', 'что именно было замечено?'], ['Theme', 'next-step visibility', 'какие альтернативы ещё возможны?'], ['Candidate change', 'блок owner + status explanation', 'что проверяем, а не обещаем?'], ['Claim', 'not-established', 'какой эффект запрещено заявлять?'], ['Follow-up', 'тот же fixed neutral scenario', 'с чем сравним следующий record?']]),
+ h2('Поля журнала и их самостоятельный смысл'),
+ p('В полевом журнале consent boundary — не ссылка на шаблон, а ответ: можно ли использовать именно эту заметку. Scenario фиксирует работу без UI-подсказки. Observation сохраняет видимое действие, code даёт ей короткое имя, uncertainty ограничивает трактовку. Decision log связывает эти элементы и формулирует follow-up. Такое разнесение делает журнал полезным, когда автор сессии недоступен: reviewer восстанавливает цепочку без пересказа встречи.'),
+ p('Термин traceability означает прослеживаемость решения назад до наблюдения. Он не равен доказанной причинности. Если карточка советует добавить owner block, reviewer должен увидеть две заметки, из которых возникла тема next-step visibility, и то, что они не устанавливают частоту или эффект. Иначе один удачный рассказ незаметно становится нормативом для всего внутреннего инструмента.'),
+ h2('Воспроизводимый тест полевого журнала'),
+ code("import { createFixedResearchInput, inspectToolUxResearch, prepareSyntheticFollowUp } from './upgrade-2025-08.mjs';\n\nconst report = inspectToolUxResearch(\n createFixedResearchInput('fixed-withdrawn-consent-v1'),\n);\nconsole.log(prepareSyntheticFollowUp(report));\n// Expected: stop-and-repair-boundary.\n// This only validates fixed literals; it does not delete or retain real research material."),
+ p('Ветка показывает важный порядок: withdrawn consent проверяется раньше темы и UI-решения. В настоящей работе реакция на отзыв определяется policy организации и может включать удаление. Скрипт ничего не удаляет и не симулирует человека; его результат только запрещает synthetic hand-off. Поэтому он не выдаёт технический пример за инструкцию по обработке реальных материалов.'),
+ h2('Порядок review для одной UX-задачи'),
+ ol(['Открыть boundary. Убедиться, что purpose, evidence type, запись и отзыв сформулированы до чтения цитат.', 'Прочитать scenario без макета. Проверить, что задача может привести и к неудобному для автора ответу.', 'Проверить notes. В каждой остались действие или вопрос, а не оценка человека.', 'Сверить codes. Метка существует в codebook и не обещает объяснить причину наблюдения.', 'Прочитать uncertainty вслух. Она должна назвать то, чего evidence не установил.', 'Разрешить следующий шаг, не результат. Decision получает follow-up на том же сценарии или STOP при сломанной ссылке.']),
+ p('Все карточки, labels и outcomes в этом журнале — fixed synthetic JavaScript literals. Нет реальных сотрудников, интервью, записей, денег, метрик, файлов, API или production-систем. Положительный результат модели означает только корректный synthetic hand-off; это не usability finding и не разрешение менять внешний или внутренний продукт.'),
+ h2('Как провести review журнала'),
+ p('Сначала reviewer читает consent boundary, а не candidate change. Если цель и разрешённый evidence неясны, полезность заметки не оправдывает её использование. Затем он читает scenario вслух и вычёркивает слова, которые называют будущий control. После этого проверяет observation: есть ли действие или вопрос, code и uncertainty. Лишь в конце он смотрит на theme и решение. Такой порядок уменьшает confirmation bias — склонность видеть только то, что поддерживает первоначальную идею.'),
+ p('Следующий тест — traceability, прослеживаемость. Для каждого предложения в decision log reviewer открывает observation id и задаёт один вопрос: «что именно здесь наблюдали?» Если ответом служит память участника встречи, связь не восстановлена. В fixed инспекторе unlinked id даёт decision-not-traceable-to-evidence. Код не заменяет совместный разбор, но делает отсутствие ссылки явным до того, как задача попадёт в backlog.'),
+ h2('Как не выдать follow-up за результат'),
+ p('Candidate change здесь предельно маленькое: добавить блок следующего шага. У него нет обещания «сократить время», «снизить обращения» или «повысить satisfaction». Следующий synthetic hand-off допускает только новую обезличенную observation record на той же задаче. Если changed scenario добавит обучение, другое исходное состояние или вопрос об оценке блока, журнал должен написать, что условия различаются. Смешивать два маршрута и называть разницу улучшением нельзя.'),
+ p('Это особенно важно для внутренних инструментов, где участники знают автора и могут помогать ему ответом. Нейтральная задача, пауза после вопроса и заметка о неизвестном не устраняют социальное давление полностью. Они дают команде место, где это давление можно заметить. Если человек соглашается с решением, но не может завершить сценарий, для интерфейса важнее наблюдаемый маршрут, а не вежливое одобрение.'),
+ h2('Граница приватности и распространения'),
+ p('GOV.UK guidance подчёркивает, что notes и recordings могут содержать personal data и должны использоваться в пределах согласия. Отсюда не следует, что слово «анонимный» снимает все риски. В настоящей среде нужно заранее определить владельца данных, место хранения, доступ, срок и процесс отзыва. В этом пакете нет ни одного реального поля или записи; synthetic labels созданы именно чтобы пример нельзя было ошибочно принять за шаблон хранения данных коллег.'),
+ h2('Ограничение и следующий шаг'),
+ p('Полевой журнал не обещает объективность и не превращает две заметки в представительную выборку. Он не измеряет деньги, производительность, качество сотрудника или эффект интерфейса. Его практическая ценность уже: плохое evidence останавливается раньше разработки, а хорошее оставляет путь для проверки. Следующий шаг: добавьте в ближайшую UX-задачу ссылки на scenario и observation, одну uncertainty и статус claim. Если хотя бы одна ссылка отсутствует, заведите follow-up research task вместо UI ticket.'),
+ h2('Историческая граница августа 2025'),
+ p('Все использованные официальные руководства были опубликованы до 31 августа 2025; список источников показывает дату и ограничение каждого. Они поддерживают дисциплину согласия, работы с заметками и исследовательских вопросов. Наша модель decision log не является универсальным стандартом, не переносит consent между организациями и не доказывает корректность гипотезы о внутреннем интерфейсе.'),
+]);
+
+export const revisions = [practice, mechanism, field].map(({ proseLength, ...item }) => item);
+if (process.argv.includes('--verify-fixture')) {
+ const assertions = runToolUxResearchFixture().assertions;
+ const failed = Object.entries(assertions).filter(([, value]) => value !== true).map(([key]) => key);
+ if (failed.length) { process.stderr.write('FAIL fixture: ' + failed.join(', ') + '\n'); process.exitCode = 1; }
+ else process.stdout.write('PASS fixture: ' + Object.keys(assertions).length + '/' + Object.keys(assertions).length + ' assertions\n');
+}
+if (process.argv.includes('--print-revisions')) process.stdout.write(JSON.stringify(revisions) + '\n');