diff --git a/editorial/production/README.md b/editorial/production/README.md index 69db4b6..253cb4d 100644 --- a/editorial/production/README.md +++ b/editorial/production/README.md @@ -1,6 +1,6 @@ # Производство редакционных партий -На 31 июля 2026 года строгий аудит проходит 322 из 358 созданных материалов. Остальные 36 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. +На 31 июля 2026 года строгий аудит проходит 325 из 358 созданных материалов. Остальные 33 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. ## Одна партия diff --git a/editorial/reviews/2027-01-draft.md b/editorial/reviews/2027-01-draft.md new file mode 100644 index 0000000..5d6515a --- /dev/null +++ b/editorial/reviews/2027-01-draft.md @@ -0,0 +1,87 @@ +# P107 — January 2027: «Десять лет web-диагностики» + +## Статус, граница времени и scope + +Редакторская дата: **31 июля 2026**. Январь 2027 ещё не наступил. Все три текста прямо обозначены как **план/сценарий на 2027-01** с source cutoff **2026-07-31**. Это не ретроспектива о завершённых десяти годах, не historical report и не описание реального incident. + +Для P107 нет bug reports, logs, traces, repro, tool output, production incidents, customer data или historical artifacts. Очередь ожидает такие артефакты, но этот пакет их не создаёт и не имитирует. Термины из официальных источников отделены от synthetic модели: source говорит о формате или словаре, но не подтверждает конкретную web-систему, carrier, запись, причинность либо результат расследования. + +Исполняемый пример принимает только named fixed in-memory literals. Factory выполняет JSON clone, затем deep freeze; evaluator принимает только JSON-равный известный literal и fail-closed закрывает всё остальное. Он не читает и не меняет сеть, browser, файлы, environment, clock, secrets, telemetry backend, external tools или production. Единственный допустимый положительный output — `synthetic-plan-hand-off` с `productionEffect: not-attempted`; root cause, fix, rollout, report, incident и любой положительный production conclusion запрещены. + +Изменены только следующие файлы: + +- `web/scripts/upgrade-2027-01.mjs` +- `editorial/reviews/2027-01-draft.md` +- `web/public/assets/editorial/2027/debugging-decade-2027-evolution-timeline.svg` +- `web/public/assets/editorial/2027/debugging-decade-2027-signal-tool-limit-table.svg` +- `web/public/assets/editorial/2027/debugging-decade-2027-hypothesis-evidence-loop.svg` + +Registry, README, `articles.json`, app, очередь, P102–P106 и Git-state не менялись. + +## Pinned primary sources и границы + +1. [W3C Trace Context](https://www.w3.org/TR/2021/REC-trace-context-1-20211123/), W3C Recommendation, 23 November 2021. Использован только для терминологической границы стандартизированного propagation trace context. Не подтверждает trace, carrier, связь или причинность в P107. +2. [RFC 5424: The Syslog Protocol](https://www.rfc-editor.org/rfc/rfc5424), March 2009, DOI `10.17487/RFC5424`. Использован только для различения формата protocol message и фактической записи. Не создаёт log record, report или history. +3. [NIST SP 800-92: Guide to Computer Security Log Management](https://doi.org/10.6028/NIST.SP.800-92), September 2006, DOI `10.6028/NIST.SP.800-92`. Использован как общий контекст дисциплины provenance и log management. Не подтверждает сбор, retention, incident или расследование. + +Все три источника первичные, pinned и не позднее cutoff. Имена fixed cases, статусы, правило «evidence не сильнее input», диаграммы, таблицы и safe hand-off — собственная synthetic модель P107, не вывод этих источников. + +## Review pass 1 — три разных редакционных фокуса + +- **Practice**: аккуратная карта от synthetic симптома к именованной гипотезе и planned signals. В первых двух абзацах названы ранний прыжок к причине и ретроспективный вымысел; цена — обратные изменения, шум и ложная память. Статья не предлагает универсальный метод и не приписывает историю конкретной команде. +- **Mechanism**: что не меняется между log, trace, metric и новым UI: named input, правило связи, альтернативное объяснение и предел силы вывода. В начале названа ложная причинность по id и цена — исключение не исключённых альтернатив. Отдельно разведены schema, поле, collector, время и evidence. +- **Field**: synthetic debugging hand-off без field artifact. В начале названа передача «причина подтверждена» без материала и цена — ложная операционная память. Таблица и схема описывают лишь форму будущей передачи; они не называют project, tool, person, incident, время или production effect. +- Голос M10: короткая прагматичная русская техническая речь наставника, с прямыми ограничениями и без обещания одного метода для всех систем. + +## Review pass 2 — literal safety и temporal integrity + +- Положительный `planned-handoff-v1` требует `planDate: 2027-01`, `sourceCutoff: 2026-07-31`, named symptom/hypothesis/signals и `evidence: synthetic-contract/input/not-collected`. +- `undated-scenario-v1` → `stop-undated-scenario-or-cutoff`. +- `unnamed-signal-v1` → `stop-unnamed-signal`; `unnamed-hypothesis-v1` → `stop-unnamed-symptom-or-hypothesis`. +- `evidence-stronger-than-input-v1` → `stop-evidence-stronger-than-input`: synthetic plan не может заявить `observed` или `claimed-collected` evidence. +- `positive-conclusion-v1` → `stop-disallowed-positive-conclusion`: `root-cause-confirmed` запрещён. +- Factory использует JSON clone и deep freeze, включая nested `signals`. Произвольный или изменённый input → `stop-unknown-fixed-input`. Допустимая ветка возвращает только `synthetic-plan-hand-off`, `productionEffect: not-attempted` и future evidence owner action. + +## Review pass 3 — visuals, доступность, неповторяемость + +- У каждого SVG есть русские `title` и `desc`, а у соответствующей статьи — русские meaningful alt и caption. Изображения содержат механизм: timeline с двумя stop, сравнительную таблицу пределов inference и loop hand-off. +- XML validation прошла для всех трёх SVG. Safety scan не нашёл `script`, `foreignObject`, `javascript:`, `data:image` или inline event handlers; внешних ссылок в SVG нет. +- Sharp создал три PNG шириной 375 px. Ручной visual inspection подтвердил: заголовки и подписи не обрезаны; practice сохраняет все четыре узла и два stop; mechanism читает все четыре строки таблицы и красные запреты; field сохраняет направления синей и красных стрелок и читаемые stop-блоки. +- Строгий pairwise scan исключал section sources, но включал code. Общих точных фрагментов по 12 слов: practice/mechanism `0`, practice/field `0`, mechanism/field `0`. + +## Actual verification commands and results + +Запущено из `web/`: + +```sh +node --check scripts/upgrade-2027-01.mjs +node scripts/upgrade-2027-01.mjs --verify-fixture +npm run audit:draft -- scripts/upgrade-2027-01.mjs +node --input-type=module -e "import { createFixedDiagnosticCase, assessFixedDiagnosticPlan } from './scripts/upgrade-2027-01.mjs'; const ids=['planned-handoff-v1','undated-scenario-v1','unnamed-signal-v1','unnamed-hypothesis-v1','evidence-stronger-than-input-v1','positive-conclusion-v1']; for (const id of ids) console.log(id, assessFixedDiagnosticPlan(createFixedDiagnosticCase(id)).status); const accepted=assessFixedDiagnosticPlan(createFixedDiagnosticCase()); console.log('exports-frozen', Object.isFrozen(createFixedDiagnosticCase()), Object.isFrozen(createFixedDiagnosticCase().signals), accepted.productionEffect);" +xmllint --noout public/assets/editorial/2027/debugging-decade-2027-evolution-timeline.svg public/assets/editorial/2027/debugging-decade-2027-signal-tool-limit-table.svg public/assets/editorial/2027/debugging-decade-2027-hypothesis-evidence-loop.svg +! rg -n -i "<(script|foreignObject)\\b|javascript:|data:image|(?:^|[[:space:]])on[a-z]+=" public/assets/editorial/2027/debugging-decade-2027-evolution-timeline.svg public/assets/editorial/2027/debugging-decade-2027-signal-tool-limit-table.svg public/assets/editorial/2027/debugging-decade-2027-hypothesis-evidence-loop.svg +node --input-type=module -e "import sharp from 'sharp'; for (const n of ['debugging-decade-2027-evolution-timeline','debugging-decade-2027-signal-tool-limit-table','debugging-decade-2027-hypothesis-evidence-loop']) await sharp('public/assets/editorial/2027/'+n+'.svg').resize({width:375}).png().toFile('/private/tmp/'+n+'-375.png');" +node --input-type=module -e "import { revisions } from './scripts/upgrade-2027-01.mjs'; const text=h=>h.replace(/
' + text + '
'; +const h2 = (text) => '' + escapeHtml(text) + '';
+const ol = (items) => '| ' + cell + ' | ').join('') + '
|---|
| ' + cell + ' | ').join('') + '
synthetic-plan-hand-off с productionEffect: not-attempted.'),
+ h2('Диагностика начинается с формы вопроса'),
+ p('За десять лет web-диагностики изменились интерфейсы наблюдения, форматы контекста и привычки команд. Не изменилась одна граница: симптом не равен причине. Симптом — короткое, наблюдаемое описание расхождения, например «результат маршрута не совпадает с ожидаемым вопросом». Гипотеза — отдельное объяснение, которое можно опровергнуть. Между ними нужна карта: какие сигналы вообще могли бы различить альтернативы, какой владелец вправе их собирать и что останется неизвестным после каждого шага.'),
+ p('В этой статье карта намеренно synthetic. Имена route-result-mismatch и context-boundary-unmapped — fixed literals, не названия реального endpoint, сбоя или следа. Они полезны ровно тем, что не дают дорисовать детали. Хорошая карта не обещает универсальный метод: иногда первым нужен контракт API, иногда граница клиента и сервера, иногда вопрос должен быть снят как плохо поставленный. Здесь мы тренируем дисциплину связей, а не рассказываем о случившемся случае.'),
+ figure('/assets/editorial/2027/debugging-decade-2027-evolution-timeline.svg', 'Временная шкала плановой карты диагностики: от симптома через именованную гипотезу и плановые сигналы к synthetic hand-off; красные стопы отмечают неназванный сигнал и чрезмерный вывод.', 'Схема показывает изменения языка и инструментов как контекст для вопроса, но не утверждает историю конкретной команды, incident или набора артефактов.'),
+ table('Карта вопроса без выдуманного evidence', ['Узел', 'Что фиксируем в плане', 'Что он различает', 'Чего не разрешено говорить'], [
+ ['Симптом', 'id и synthetic question', 'что именно не совпадает', 'что событие наблюдалось'],
+ ['Гипотеза', 'id и alternative question', 'какое объяснение проверяется', 'что причина подтверждена'],
+ ['Сигнал', 'имя и роль planned-input', 'какой будущий input нужен', 'что лог или trace уже существует'],
+ ['Evidence contract', 'synthetic-contract, input', 'предел будущей передачи', 'что evidence сильнее входа'],
+ ['Выход', 'synthetic-plan-hand-off', 'кому передать вопрос', 'что разрешён production change'],
+ ]),
+ h2('Сигнал — не синоним данных'),
+ p('Слово «сигнал» легко маскирует незнание. В плановом record signal — это имя будущего input и его роль, а не запись в журнале. request-context-carrier не доказывает, что заголовок дошёл; named-event-record не доказывает, что событие было записано, сохранено или доступно. Такая разница скучна только пока не приходится объяснять, почему одна и та же строка кажется одновременно отсутствующим контекстом, ошибкой парсинга и задержкой доставки.'),
+ p('Нужно держать минимум три границы. Первая: значение может отсутствовать, и это ещё не означает дефект. Вторая: значение может быть названо, но не иметь interpretation contract. Третья: даже корректно интерпретированное значение не доказывает гипотезу, если альтернативы не исключены. Публичные спецификации помогают говорить о переносе контекста и полях сообщения, однако они не подтверждают наш synthetic route и не назначают нам порядок расследования.'),
+ h2('Гипотеза должна проигрывать честно'),
+ p('У полезной гипотезы есть возможный проигрыш. «Что-то потерялось на границе» удобно звучит, но не даёт следующего действия: граница не названа, альтернативы не названы, любой исход можно объявить совместимым с фразой. context-boundary-unmapped уже уже: он просит будущего владельца назвать boundary и carrier, но не утверждает, что контекст когда-либо передавался. Если гипотезу нельзя сделать слабее от будущего input, это не гипотеза, а название тревоги.'),
+ p('Эта строгость особенно нужна после долгой истории инструментов. Новый trace UI может дать ощущение причинности за счёт красивой связки, старый лог — ощущение простоты за счёт одной строки. Ни интерфейс, ни формат не отменяют inference boundary. Сначала формулируют, какая разница между вариантами имеет значение; затем определяют minimum input; потом отдельно решают, кому разрешено получить реальный evidence. В сценарии до последнего шага не происходит ничего, кроме проверки литерала.'),
+ h2('Литеральная карта безопасного маршрута'),
+ code("import { createFixedDiagnosticCase, assessFixedDiagnosticPlan } from './upgrade-2027-01.mjs';\n\nconst routeQuestion = createFixedDiagnosticCase('planned-handoff-v1');\nconst disposition = assessFixedDiagnosticPlan(routeQuestion);\nconsole.log(JSON.stringify([\n { field: 'kind', value: disposition.status },\n { field: 'effect', value: disposition.productionEffect },\n { field: 'owner-question', value: disposition.nextAction },\n]));\n// [{\"field\":\"kind\",\"value\":\"synthetic-plan-hand-off\"},{\"field\":\"effect\",\"value\":\"not-attempted\"},{\"field\":\"owner-question\",\"value\":\"give-the-fixed-question-contract-to-a-future-evidence-owner\"}]"),
+ p('Пример импортирует только эту модель и получает заранее именованную структуру из памяти. Клонирование изолирует её от исходного набора, а рекурсивная заморозка сохраняет форму во время вызова. Evaluator принимает лишь известный literal; произвольный object закрывается. Код не читает сеть, browser, файлы, environment, часы, secrets, telemetry или внешний инструмент. Он не генерирует report, не собирает trace и не создаёт repro. Его положительный результат — hand-off формы вопроса, не диагноз и не разрешение менять систему.'),
+ h2('Порядок подготовки карты на 2027-01'),
+ ol(['Явно пометить материал как план/сценарий на 2027-01 и рядом оставить source cutoff 2026-07-31.', 'Назвать один synthetic симптом без URL, пользователя, времени, incident или правдоподобной истории.', 'Записать одну гипотезу как опровержимое объяснение и отдельно назвать альтернативу, которую она не покрывает.', 'Дать каждому будущему сигналу id и роль planned-input; не подменять имя записью или метрикой.', 'Оставить evidence на уровне synthetic-contract/input/not-collected и проверить fail-closed stops.', 'Передать только synthetic-plan-hand-off будущему владельцу evidence, не объявляя root cause, fix или production effect.']),
+ h2('Карта полезна, когда сужает следующий вопрос'),
+ p('В диагностике легко измерять активность вместо продвижения: добавить полей, нарисовать больше стрелок, попросить «полный trace». Карта должна выдерживать обратный вопрос: какое решение станет другим, если будущий signal окажется пустым, неоднозначным или недоступным? Если ничего не меняется, signal пока не относится к гипотезе. Его стоит убрать из contract, а не собирать на всякий случай. Так уменьшается не только объём будущих данных, но и число историй, которые можно случайно прочитать в одном и том же наборе полей.'),
+ p('Есть полезная экономия и в порядке. Не надо сначала выбирать самый мощный инструмент, затем искать, что он мог бы показать. Сначала достаточно назвать smallest discriminating question: например, требуется ли вообще carrier на конкретной, пока synthetic границе. Если ответ нет, разговор о схеме записи и поиске не начинается. Если ответ да, будущий owner получает узкое требование, а не просьбу добыть всё. Это не правило для каждой системы; это способ не оплачивать наблюдаемость до того, как появился предмет различения.'),
+ h2('Почему историческая тема не даёт исторических прав'),
+ p('Название «десять лет» может соблазнить общими выводами: будто практика неизбежно движется от логов к trace или будто новый инструмент обязательно делает команду точнее. В источниках нет такого результата для нашего выпуска, а у редакции нет собственного корпуса case studies. Можно сказать скромнее: за период развивались стандарты и способы описывать контекст; мы используем их как словарь. Нельзя сказать, что конкретная организация прошла этот путь, быстрее находила дефекты или завершила десятилетний цикл улучшений.'),
+ p('Эта разница важна для читателя в будущем. Когда он откроет номер после января, дата публикации не должна превращать preparation в report. Source cutoff сохраняет, какие документы были допустимы при подготовке, но не заменяет факт. План сохраняет вопрос, но не событие. Если появится реальный материал, его provenance, дата, авторизация и ограничения должны быть записаны отдельно. Нельзя приклеить его к fixed literal и объявить, что он всегда имелся в виду.'),
+ h2('Ограничения карты и следующий шаг'),
+ p('Карта не измеряет задержку, полноту, sampling, retention, privacy impact или полезность инструмента. Она не говорит, какой signal лучше, и не делает любой named signal доступным. JSON clone и freeze — свойства учебного JS literal, а не защита реальной телеметрии. RFC 5424 описывает protocol message, а W3C Trace Context — перенос trace context; ни один источник не подтверждает наш симптом, существование конкретного carrier или причинность между ними.'),
+ p('Следующий шаг возможен только в будущем отдельном scope: назначить владельца evidence и определить разрешённые реальные inputs. До этого разумная работа — добавить ещё один negative fixed case и проверить точный stop. Не надо превращать этот draft в юбилейный отчёт задним числом. Десять лет здесь — тема планового выпуска, а не доказательство завершённой десятилетней ретроспективы.'),
+], [
+ { key: 'traceContext', use: 'Использован узкий факт о стандартизованном переносе trace context между границами.', boundary: 'Не подтверждает передачу, trace или причинность в synthetic сценарии.' },
+ { key: 'rfc5424', use: 'Использована терминология protocol message для отличия named signal от фактически полученной записи.', boundary: 'Не создаёт log record и не подтверждает его содержание.' },
+ { key: 'nistLogs', use: 'Использована общая дисциплина управления журналами как фон для разделения сбора и интерпретации.', boundary: 'Не является evidence о системе, incident или будущем расследовании.' },
+]);
+
+const mechanism = revision({
+ slug: 'editorial-2027-01-mechanism-debugging-decade', title: 'Десять лет web-диагностики: что сохраняется между логом и trace', categories: ['Архитектура', 'Наблюдаемость'], cover: '/assets/editorial/2027/debugging-decade-2027-signal-tool-limit-table.svg', excerpt: 'План на январь 2027: границы inference, которые не отменяют ни логи, ни trace.', readingMinutes: 20,
+}, [
+ p('План на январь 2027 рассматривает конкретную проблему механики: лог и trace кладут рядом, после чего связь по id начинают читать как доказанную причину. Цена ошибки — ложное сужение поиска. Инженер исключает альтернативы, которых вход не исключал, и тратит время на исправление уверенного, но невыведенного объяснения.'),
+ p('Есть и более тихая проблема: инструмент меняется, а сила вывода будто растёт сама. В действительности у нас нет production log, trace, trace-id, span, backend, report или реального времени. Цена выдуманного артефакта — путаница между термином из спецификации и фактом конкретной среды. Этот текст — план/сценарий на 2027-01 с source cutoff 2026-07-31; положительная ветка остаётся synthetic-plan-hand-off, а productionEffect: not-attempted.'),
+ h2('Что может пережить смену инструмента'),
+ p('Инструменты меняют разрешение и стоимость поиска, но не отменяют три простые обязанности inference. Нужны названный input, правило связи и альтернативное объяснение. Если нет input, нет факта для интерпретации. Если нет правила связи, совпадение идентификатора остаётся совпадением. Если нет альтернативы, любая картинка легко становится подтверждением любимой версии. Эти обязанности не универсальный метод расследования; это минимальные вопросы, которые не дают выдать удобство UI за доказательство.'),
+ p('Лог обычно удобен для локального сообщения, trace — для связки работ в пределах заданного контекста. Но оба формата несут границы: запись может быть неполной, контекст — отсутствовать, а связь — не покрывать работу вне carrier. W3C Trace Context определяет формат propagation, не модель причинности. RFC 5424 описывает структуру syslog message, не полноту истории. Именно поэтому synthetic model ниже не хранит образцы этих объектов: она хранит только вопросы, которые будущий owner обязан уточнить до любой попытки вывода.'),
+ figure('/assets/editorial/2027/debugging-decade-2027-signal-tool-limit-table.svg', 'Таблица сравнивает плановые роли лога, trace и ручного вопроса: каждый инструмент может дать вход, но ни один не снимает границы inference; красная колонка перечисляет запрещённые выводы.', 'Схема не содержит данных, производственных инструментов или результатов. Она отделяет терминологию источников от synthetic механизма P107.'),
+ table('Инструмент и предел вывода', ['Форма input', 'Что может быть названо', 'Что ещё требуется', 'Что не следует выводить'], [
+ ['Логовое сообщение', 'record schema и поле', 'provenance, полнота, interpretation', 'что событие было единственным'],
+ ['Trace context', 'carrier и связь контекста', 'граница propagation и альтернативы', 'что связь означает причинность'],
+ ['Метрика', 'агрегированный вопрос', 'окно, population и расчёт', 'что известен конкретный путь'],
+ ['Synthetic contract', 'будущий input', 'отдельный scope и owner', 'что evidence уже собран'],
+ ]),
+ h2('Связь не равна объяснению'),
+ p('Связь полезна, когда она уменьшает число допустимых моделей, а не когда она просто выглядит правдоподобно. Даже в будущем случае общий идентификатор мог бы означать propagation, повторное использование, ошибочную корреляцию или неполное соединение двух потоков. Чтобы выбрать между ними, нужны конкретные правила и наблюдения. Их нет, поэтому fixed case запрещает evidence сильнее input. Значение observed не «почти проходит»: оно закрывается, потому что план не имеет права тайно превратиться в collection.'),
+ p('Этот запрет удерживает важную асимметрию. Вход может разрешить задать вопрос, но не обязан разрешать утверждение. Выход может быть слабее входа: stop часто честнее hand-off. Выход не может быть сильнее входа без новой, явно авторизованной работы. Поэтому evidence-stronger-than-input-v1 является негативным fixture, а не примером зрелости. Он не проверяет реальный trace; он проверяет, что слова «наблюдалось» не проникли в учебную карточку через одно поле.'),
+ h2('Поле и схема должны быть разными объектами'),
+ p('Схема говорит, как назвать поле; поле в конкретной записи было бы evidence. Смешать их особенно легко с документацией: прочитав normative описание carrier, автор начинает писать так, будто carrier присутствовал в его маршруте. Это неверный переход. Первичный источник полезен для терминов, версии и границы формата. Он не даёт разрешения переносить свойства specification на неизвестный deployment, browser или прокси. Наша редакционная модель добавляет статусы и fail-closed rule самостоятельно и не приписывает их стандартам.'),
+ p('То же относится к старым и новым инструментам. Более удобный поиск не снимает вопрос retention; более подробная визуализация не снимает вопрос sampling; единый trace-id не снимает вопрос ownership границы. Практичный наставник не запрещает инструмент. Он спрашивает: какое конкретное решение этот input мог бы изменить и какое наблюдение оставит решение прежним? Если ответа нет, модель ещё не готова к измерению, зато готова к stop с точным следующим действием.'),
+ h2('Запускаемый validator границы inference'),
+ code("import { createFixedDiagnosticCase, assessFixedDiagnosticPlan } from './upgrade-2027-01.mjs';\n\nconst plan = createFixedDiagnosticCase('evidence-stronger-than-input-v1');\nconst result = assessFixedDiagnosticPlan(plan);\nconsole.log({ status: result.status, reason: result.reason });\n// { status: 'stop-evidence-stronger-than-input', reason: 'a-plan-cannot-contain-observed-or-collected-evidence' }"),
+ p('Это literal runnable example. Он не открывает exporter, не создаёт span и не читает журнал. В памяти существуют только фиксированные строки. JSON clone отделяет case от константы, deep freeze делает вложенные значения неизменяемыми, а проверка JSON-равенства не принимает похожий объект. Так код демонстрирует policy: разница между «можно назвать будущий signal» и «можно сказать, что что-то наблюдалось» проверяется до того, как текст превратится в отчёт.'),
+ h2('Порядок разбора механики'),
+ ol(['Зафиксировать 2027-01 и source cutoff 2026-07-31; не выдавать будущую дату за свершившуюся историю.', 'Назвать symptom как вопрос, а hypothesis — как отдельное опровержимое объяснение.', 'Для каждого signal записать, является ли он будущим input, а не существующим log или trace.', 'Отделить формат и propagation rule из источника от утверждения о конкретном runtime.', 'Проверить, что evidence имеет только synthetic-contract/input/not-collected; observed и collected должны закрываться.', 'Отдать статус hand-off или stop будущему evidence owner, не формулируя root cause, fix, incident или production conclusion.']),
+ h2('Время не возникает из порядка строк'),
+ p('Ещё одна граница, которую не снимает ни лог, ни trace, — время. Порядок прихода записей не равен порядку работы; timestamp не равен общей шкале; длительность одного участка не объясняет ожидание в другом. В P107 нет timestamp вовсе, поэтому нельзя даже начать говорить о latency, race или window. Это полезная пустота. Она не позволяет подменить отсутствие temporal model красивой временной линией и не заставляет будущего owner доказывать обратное тому, чего план не должен был утверждать.'),
+ p('Если будущему scope понадобится время, ему придётся отдельно назвать clock boundary, единицу, способ связывания и допустимую неопределённость. Даже после этого останется вопрос, что именно означает интервал: работа, ожидание, очередь или наблюдение collector. Такая детализация не нужна текущему literal, потому что он не сравнивает интервалы. Добавлять число только ради реалистичности было бы ошибкой того же класса, что и выдумать trace-id: syntactic detail не превращается в evidence от того, что выглядит знакомо.'),
+ h2('Collector не является нейтральным окном'),
+ p('Между приложением и тем, что увидит человек, обычно есть граница преобразования, отбора или хранения. Даже не описывая конкретный pipeline, полезно помнить: формат на отправителе и форма на экране могут быть разными объектами. Поэтому слова «лог сказал» или «trace показал» всегда требуют вопроса о provenance. В synthetic plan этот вопрос не решается и не маскируется названием vendor. В нём остаётся только named signal, потому что любое утверждение о collector уже было бы эмпирическим фактом без источника.'),
+ p('Здесь проявляется отличие механизма от инструкции. Инструкция могла бы велеть включить экспорт или открыть консоль. Механизм описывает, почему такое действие ещё не даёт сильного вывода. Он не запрещает будущую работу, а удерживает её границы: новый input должен прийти с новой ответственностью за сбор, доступ и interpretation. Пока такой ответственности нет, precise stop лучше общего оптимизма. Это не недоверие к инструменту; это отказ давать ему полномочие, которого не передавали. Такой предел полезнее уверенного, но непроверяемого объяснения.'),
+ h2('Ограничения механики и следующий шаг'),
+ p('Модель не оценивает качество log schema, стоимость trace backend, доступ, персональные данные, clock skew или порядок доставки. Она не говорит, какой из инструментов выбрать, и не проверяет соответствие implementation спецификации. Она также не может определить причинность по имени гипотезы. Всё это требует реальных входов и другого полномочия. Здесь fail-closed нужен не для драматичности, а чтобы отсутствие таких входов оставалось видимым.'),
+ p('Следующий шаг — будущему владельцу сформулировать отдельный evidence contract: какие input разрешены, по какому правилу они будут связаны и какой контрпример остановит вывод. Пока этого нет, не стоит расширять словарь статусов. Особенно не следует называть synthetic output «проверенным решением»: механизм проверяет только связность планового языка. Он не завершает декаду диагностики и не создаёт новый факт о web-системе.'),
+], [
+ { key: 'traceContext', use: 'Использован только факт существования стандартного формата trace-context propagation.', boundary: 'Не подтверждает trace, carrier или причинную связь конкретного сценария.' },
+ { key: 'rfc5424', use: 'Использована граница формата syslog message для различения схемы и фактической записи.', boundary: 'Не доказывает полноту, доставку или содержание журналов.' },
+ { key: 'nistLogs', use: 'Использован общий контекст log management для отделения collection от interpretation.', boundary: 'Не подтверждает incident, retention или работу конкретного инструмента.' },
+]);
+
+const field = revision({
+ slug: 'editorial-2027-01-field-debugging-decade', title: 'Десять лет web-диагностики: synthetic hand-off вместо кейса', categories: ['Полевые заметки', 'Инженерные решения'], cover: '/assets/editorial/2027/debugging-decade-2027-hypothesis-evidence-loop.svg', excerpt: 'План на январь 2027: как передать будущий вопрос диагностики, не сочинив полевой артефакт.', readingMinutes: 20,
+}, [
+ p('Самая опасная поломка field-заметки — передать получателю фразу «причина подтверждена», когда передавать нечего кроме наброска вопроса. Цена ошибки — ложная операционная память: новый инженер защищает несуществующий report, ищет отсутствующий trace и строит дальнейшее решение на авторитете старой формулировки.'),
+ p('Вторая проблема — назвать synthetic карточку реальным hand-off и добавить к ней правдоподобные время, среду, пользователя или инструмент. У нас нет исторического incident, bug report, logs, traces, repro и production-артефактов; очередь их требует, но создавать их текстом нельзя. Цена такой детализации — невозможность отличить факт от иллюстрации. Поэтому это план/сценарий на 2027-01 с source cutoff 2026-07-31 и только synthetic-plan-hand-off / productionEffect: not-attempted.'),
+ h2('Field здесь — форма передачи, не отчёт с поля'),
+ p('Слово field часто обещает место, следы, людей и последствия. В этом выпуске оно означает более скромную вещь: карточку, которую можно отдать будущему владельцу evidence, не добавляя evidence от себя. В карточке есть дата плана, cutoff, synthetic symptom, synthetic hypothesis, named planned signals, сила evidence и следующий вопрос. В ней нет URL, customer payload, временной линии, dashboard, tool output, идентификатора задачи или фразы о том, что кто-то уже начал работу.'),
+ p('Это не уход от практики. Наоборот, честная передача экономит время будущей практики. Получатель сразу видит, что может проверить форму вопроса, вернуть stop или открыть новый scope с полномочием на реальные данные. Он не должен угадывать, какие строки были художественным сокращением. В зрелой технической речи отсутствие artefact тоже имеет provenance: not-collected означает не «плохо собрали», а «в данном synthetic плане сбор не происходил и не заявляется».'),
+ figure('/assets/editorial/2027/debugging-decade-2027-hypothesis-evidence-loop.svg', 'Петля synthetic hand-off: симптом и гипотеза задают именованные плановые сигналы; затем либо stop при отсутствующем имени или чрезмерном evidence, либо передача вопроса будущему владельцу без production effect.', 'Диаграмма показывает только процесс проверки fixed literals. Она не изображает реальный incident, trace, log, пользователя или завершённое расследование.'),
+ table('Содержимое synthetic hand-off', ['Поле', 'Допустимое fixed значение', 'Получатель может сделать', 'Получатель не должен заключать'], [
+ ['Дата и cutoff', '2027-01 / 2026-07-31', 'проверить временную границу', 'что выпуск уже завершён'],
+ ['Симптом и гипотеза', 'named synthetic questions', 'уточнить альтернативы', 'что есть root cause'],
+ ['Signals', 'named planned-input', 'назвать нужный future input', 'что запись доступна'],
+ ['Evidence', 'synthetic-contract/input/not-collected', 'открыть другой scope', 'что был сбор или анализ'],
+ ['Output', 'synthetic-plan-hand-off', 'принять либо вернуть contract', 'что допустимо менять production'],
+ ]),
+ h2('Передача начинается с того, чего в ней нет'),
+ p('Обычно hand-off читают как список выполненного. Здесь полезнее начать с запрещённых подмен. Нет «вчера», потому что дата плана уже названа. Нет «пользователь увидел», потому что пользователей в literal нет. Нет «trace показал», потому что trace не создавался. Нет «инструмент не нашёл», потому что инструмент не запускался. Нет «исправление помогло», потому что исправления не было. Эти отрицания не делают карточку богаче, но сохраняют у будущего reviewer возможность поставить реальный вопрос без конфликта с вымышленным прошлым.'),
+ p('Полезная карточка не требует доверия к автору. Она содержит fixed literals, поэтому любой читатель может взять named case и получить тот же status. Но одинаковый status не делает контекст одинаковым с чужой системой. Детерминизм здесь локален: evaluator согласованно классифицирует словарь, а не воспроизводит world. Именно поэтому positive result не называется approval. Он лишь говорит, что в этом словаре есть дата, names и достаточная честность, чтобы передать вопрос дальше.'),
+ h2('Fail-closed защищает получателя'),
+ p('Отсутствующее имя сигнала — не мелкая опечатка. Без имени невозможно сказать, какой input запрашивается, а значит следующий человек получает просьбу «посмотрите что-нибудь». Отсутствующая гипотеза столь же опасна: она превращает любой signal в украшение. Сценарий без даты и cutoff лишает получателя границы источников. Эти три случая не дополняются default-значениями. Evaluator возвращает stop с точной причиной, потому что заполнять пробел удобной выдумкой означает перекладывать долг на человека, который этого не выбирал.'),
+ p('Отдельный риск — evidence, сильнее входа. Слово observed выглядит естественно для field-ярлыка, но в данной карточке оно ложно. Если реальное наблюдение когда-нибудь появится, оно должно прийти из нового процесса с известным provenance, access и правилами интерпретации. Нельзя улучшить старый synthetic input только тем, что поменяли строку. Наконец, root-cause-confirmed запрещён независимо от красоты names: положительный диагноз потребовал бы доказательств, которых модель специально не содержит.'),
+ h2('Литеральный hand-off и его отрицательная ветка'),
+ code("import { createFixedDiagnosticCase, assessFixedDiagnosticPlan } from './upgrade-2027-01.mjs';\n\nconst plan = createFixedDiagnosticCase('positive-conclusion-v1');\nconst result = assessFixedDiagnosticPlan(plan);\nconsole.log({ status: result.status, next: result.nextAction });\n// { status: 'stop-disallowed-positive-conclusion', next: 'use-synthetic-plan-hand-off' }"),
+ p('Этот example literally runs without a live system. Он читает только in-memory named literals; JSON clone исключает разделённую ссылку, deep freeze фиксирует все вложенные поля. В нём нет файлового fixture, переменной окружения, часов, сетевого вызова или API наблюдаемости. Проверяемый результат — отказ от чрезмерного вывода. Это важно для hand-off: получатель видит, что автор не оставил скрытую позитивную ветку, которая могла бы превратить план в готовое заключение.'),
+ h2('Порядок передачи январского сценария'),
+ ol(['Оставить на карточке явные planDate: 2027-01 и sourceCutoff: 2026-07-31.', 'Назвать symptom и hypothesis как synthetic вопросы, не добавляя историю, источник или реальный эффект.', 'Проверить, что каждый signal имеет имя и planned-input; unnamed signal вернуть с stop.', 'Оставить evidence только synthetic-contract/input/not-collected; observed, collected и stronger claims закрыть.', 'Проверить, что conclusion равен только synthetic-plan-hand-off и effect всегда not-attempted.', 'Передать boundary и nextAction будущему evidence owner как вопрос, не как queue task, approval или incident conclusion.']),
+ h2('Получатель должен суметь не согласиться'),
+ p('Хорошая передача не заставляет получателя принять исходную framing. Он должен суметь вернуть карточку с фразой: symptom слишком широкий, гипотеза не различает альтернативы, signal не имеет владельца, а source cutoff не подходит новому вопросу. Такое несогласие — не отказ от hand-off, а его штатный результат. Если карточка принимает только одобрение, она становится маленькой очередью с замаскированным приоритетом. P107 специально не создаёт queue и не назначает срок, чтобы не выдать организационное действие за техническую проверку.'),
+ p('По этой же причине nextAction сформулирован как передача fixed question contract, а не как «соберите данные». Последняя фраза предполагает полномочие, доступ и цель сбора. Первая сохраняет выбор у будущего owner. Он может признать, что evidence не нужен, или открыть новый authorized scope, или запросить другой model. Synthetic hand-off не теряет ценность от такого результата: он честно ограничивает то, что было известно на дату подготовки, и даёт получателю опору, не создавая несуществующей обязанности.'),
+ h2('Как карточка стареет без подмены фактов'),
+ p('После source cutoff документы, термины и реализации могут измениться. Это не делает старую карточку неправильной, но меняет её статус: она остаётся записью о подготовленном вопросе, не справочником о текущей системе. Будущий reviewer должен обновить источники в новом scope, а не молча приписать свежие сведения январскому плану. Точно так же нельзя задним числом заменить not-collected на «наблюдалось», даже если кто-то позднее действительно собрал материал. Это были бы разные artefacts с разной датой и provenance.'),
+ p('Долговечность hand-off достигается не количеством контекста, а явными границами. Полезно, когда через год можно увидеть, почему statement был слабым, какие слова нельзя было использовать и где требовалась новая авторизация. Вредно, когда старая карточка выглядит настолько конкретно, что её принимают за архив. Поэтому в поле нет правдоподобных названий проектов, систем и инцидентов. Отсутствие таких деталей — не пробел редактора, а часть safety contract для читателя, который появится позже.'),
+ h2('Ограничения hand-off и следующий шаг'),
+ p('Карточка не назначает владельца, не создаёт ticket, не отправляет уведомление и не меняет очередь. Она не оценивает безопасность, приватность, доступность данных, retention, бюджет или пригодность существующих инструментов. Она также не покрывает historical reports/artifacts, которых у P107 нет. Технические источники закрепляют значение терминов, но не заполняют этот дефицит. Называть absence «архивом» было бы той же подменой, что и сочинить incident.'),
+ p('Следующий шаг — будущему владельцу решить, нужен ли отдельный authorized discovery и какие реальные evidence допустимы. Если такого решения нет, hand-off остаётся конечным безопасным результатом. Для самого draft следующий шаг проще: добавить новый negative literal, например с неназванным symptom, и сохранить fail-closed ответ. Не усиливайте этот текст retrospective тоном. Январь 2027 ещё впереди относительно редакторской даты, а десятилетие здесь — только тема заранее подготовленного номера.'),
+], [
+ { key: 'traceContext', use: 'Использована только терминологическая граница propagation context для будущего evidence contract.', boundary: 'Не создаёт trace, incident или доступ к какому-либо carrier.' },
+ { key: 'rfc5424', use: 'Использована граница protocol message как напоминание, что schema не равна фактической записи.', boundary: 'Не подтверждает logs, reports или historical artifacts.' },
+ { key: 'nistLogs', use: 'Использована общая дисциплина provenance журналов как причина не выдумывать их в hand-off.', boundary: 'Не доказывает существование процесса сбора или расследования.' },
+]);
+
+export const revisions = deepFreeze([practice, mechanism, field]);
+export function verifyRevisionsAgainstFixture() {
+ const fixture = runFixedDiagnosticFixture();
+ const articleChecks = revisions.map((revisionItem) => { const text = bodyText(revisionItem.contentHtml); return text.length >= 5000 && text.length <= 15000 && /(цен[аы]|стоимост|издержк|потер)/i.test(text.slice(0, 1000)) && /