From eaca8e28a55d2ed73d87f1c807f84f690ede52b3 Mon Sep 17 00:00:00 2001 From: "E.Gavrilov" Date: Fri, 31 Jul 2026 19:57:54 +0300 Subject: [PATCH] revise November 2026 technology evaluation articles --- editorial/production/README.md | 2 +- editorial/reviews/2026-11-draft.md | 65 ++++++ web/data/editorial-revisions.mjs | 2 + ...-evaluation-2026-evidence-handoff-loop.svg | 10 + ...logy-evaluation-2026-tradeoff-frontier.svg | 9 + ...aluation-2026-weighted-decision-matrix.svg | 12 + web/scripts/upgrade-2026-11.mjs | 208 ++++++++++++++++++ 7 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 editorial/reviews/2026-11-draft.md create mode 100644 web/public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg create mode 100644 web/public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg create mode 100644 web/public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg create mode 100644 web/scripts/upgrade-2026-11.mjs diff --git a/editorial/production/README.md b/editorial/production/README.md index 27e3f44..4cbf6d0 100644 --- a/editorial/production/README.md +++ b/editorial/production/README.md @@ -1,6 +1,6 @@ # Производство редакционных партий -На 31 июля 2026 года строгий аудит проходит 316 из 358 созданных материалов. Остальные 42 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. +На 31 июля 2026 года строгий аудит проходит 319 из 358 созданных материалов. Остальные 39 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. ## Одна партия diff --git a/editorial/reviews/2026-11-draft.md b/editorial/reviews/2026-11-draft.md new file mode 100644 index 0000000..768ce92 --- /dev/null +++ b/editorial/reviews/2026-11-draft.md @@ -0,0 +1,65 @@ +# P105 — November 2026: «Сравнение технологий без хайпа» + +## Статус и временная граница + +Редакторская дата: **31 июля 2026**. Ноябрь ещё не наступил. Все три текста — явно помеченные план/сценарий на **2026-11** с source cutoff **2026-07-31**. Это не отчёты о внедрении, benchmark, выборе, rollout, замере или field-работе; такие события не заявляются и не подразумеваются. + +Исполняемый пример использует только named fixed in-memory literals, JSON clone и deep freeze. Он не читает сеть, browser, файлы, environment, часы, secrets, telemetry или внешние инструменты. У единственной положительной ветки статус `synthetic-plan-hand-off`, а `productionEffect: not-attempted`; выбор, winner, измерение и rollout кодом запрещены. + +## Источники и boundary + +1. [NIST SP 800-30 Rev. 1: Guide for Conducting Risk Assessments](https://doi.org/10.6028/NIST.SP.800-30r1), Revision 1, September 2012, DOI `10.6028/NIST.SP.800-30r1`. Использован как первичный источник терминов о risk assessment и uncertainty. Он не задаёт наши weights, не сравнивает технологии и не подтверждает benchmark. +2. [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119), BCP 14, March 1997, DOI `10.17487/RFC2119`. Использован только для дисциплины слов требования. Он не создаёт policy сравнения и не делает hand-off решением. +3. [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174), BCP 14, May 2017, DOI `10.17487/RFC8174`. Использован для уточнения контекста BCP 14. Он не подтверждает будущий rollout или измерение. + +Все источники pinned, первичные и опубликованы не позже cutoff. Их факты отделены от собственной модели P105: fixed criteria, weights 40/35/25, trade-off frontier, статусы и safe hand-off — редакционные конструкции данного плана. + +## Review pass 1 — проблема, голос, плотность, неповторяемость + +- **Practice** открывается скрытой стоимостью adoption после выбора по демо; цена — обучение, миграция и поддержка, обнаруженные поздно. Угол: взвешенная матрица с отдельным cost of adoption. +- **Mechanism** открывается ложной численной точностью неполного наблюдения; цена — потерянная конфигурация и спор с готовым «итогом». Угол: вопрос, observation, uncertainty и weight как разные сущности. +- **Field** открывается передачей «доказанного преимущества» без evidence; цена — ложная операционная память и неотменяемая история. Угол: safe synthetic evidence hand-off, а не отчёт. +- Голос M9: короткие технические утверждения, прямые ограничения, нет маркетингового обещания или общего введения. В первых двух абзацах каждой статьи названы конкретная проблема и цена. +- Независимость проверена строго попарно без списков sources, но с code: общих точных абзацев — `0`; общих фрагментов из 12 слов — `0` для practice/mechanism, practice/field и mechanism/field. + +## Review pass 2 — cutoff, факты и literal safety + +- В каждом literal требуются `planDate: 2026-11` и `sourceCutoff: 2026-07-31`; недатированный сценарий закрывается `stop-undated-plan-or-cutoff`. +- Factory отдаёт JSON clone, затем deep freeze. Только named fixed cases принимаются evaluator, произвольный input закрывается `stop-unknown-fixed-input`. +- Отдельно покрыты опасные cases: отсутствующий criterion/weight → `stop-missing-criterion-or-weight`; скрытая measurement configuration → `stop-hidden-measurement-configuration`; заявленный winner/benchmark result → `stop-disallowed-positive-result`. +- Положительный case не выбирает альтернативу: `synthetic-plan-hand-off`, `productionEffect: not-attempted`, `nextAction: give-the-fixed-plan-to-a-future-evidence-owner`. +- В code snippets нет сетевых, файловых, environment, clock, browser, secret, telemetry или external-tool APIs. «Собственный benchmark» из очереди заменён только fixed synthetic configuration со значениями `not-run` и `not-estimated`. + +## Review pass 3 — SVG mobile, XML/safety, команды + +- Каждый SVG имеет русские title/desc, data-bearing content и соответствующие русские alt/caption в статье. Макет рассчитан на 720×500 viewBox, рабочий текст 14–24 px, без декоративной подмены механизма. +- Для 375 px Sharp render должен подтвердить: matrix читает три веса и четыре stop; frontier не обрезает область неопределённости; hand-off loop сохраняет направление зелёной и красной стрелок. PNG осматривается визуально, а не только по exit code. +- SVG проходят XML validation и safety scan: отсутствуют `script`, `foreignObject`, `javascript:`, `data:image` и event handlers. В SVG нет внешних ссылок. + +## Команды проверки + +Запускать из `web/`: + +```sh +node --check scripts/upgrade-2026-11.mjs +node scripts/upgrade-2026-11.mjs --verify-fixture +npm run audit:draft -- scripts/upgrade-2026-11.mjs +node --input-type=module -e "import { createFixedTechnologyEvaluationCase, assessFixedTechnologyEvaluation } from './scripts/upgrade-2026-11.mjs'; for (const id of ['weighted-plan-v1','undated-plan-v1','missing-weight-v1','hidden-measurement-v1','claimed-benchmark-win-v1']) console.log(id, assessFixedTechnologyEvaluation(createFixedTechnologyEvaluationCase(id)).status);" +xmllint --noout public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg +rg -n -i "<(script|foreignObject)\\b|javascript:|data:image|(?:^|[[:space:]])on[a-z]+=" public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg +node --input-type=module -e "import sharp from 'sharp'; for (const n of ['technology-evaluation-2026-weighted-decision-matrix','technology-evaluation-2026-tradeoff-frontier','technology-evaluation-2026-evidence-handoff-loop']) await sharp('public/assets/editorial/2026/'+n+'.svg').resize({width:375}).png().toFile('/private/tmp/'+n+'-375.png');" +``` + +## Disposition + +Проверки P105 должны зафиксировать: syntax; fixture `8/8`; audit всех трёх slugs; literal public exports; четыре опасных stop; XML; SVG safety; Sharp 375 px и ручной visual inspection; строгую pairwise uniqueness с включённым code и исключёнными sources. Этот draft не меняет registry, README, articles.json, очередь, application files, чужие draft-пакеты или Git-state. + +## Независимая приёмка основного редактора — 31.07.2026 + +Принято только как **плановое сравнение на ноябрь 2026**. Тексты явно называют `2026-11` и source cutoff `2026-07-31`; fixture не выбирает технологию, не запускает benchmark и не допускает rollout. Единственный positive output — `synthetic-plan-hand-off` c `productionEffect: not-attempted`. + +Проверен NIST SP 800-30 Rev. 1: документ действительно датирован September 2012, называет assessment неполным измерительным инструментом и описывает ограничения данных, методик и интерпретации. RFC 2119 и RFC 8174 подтверждают даты/контекст BCP 14. Источники оставлены только для терминов риска и силы требований; weights 40/35/25, frontier и статусы прямо отмечены как редакционная synthetic model, а не как рекомендация NIST или RFC. + +Повторные проверки: syntax; fixture `8/8`; draft audit с объёмами 9 643 / 9 587 / 9 685; literal outputs `synthetic-plan-hand-off`, `stop-undated-plan-or-cutoff`, `stop-missing-criterion-or-weight`, `stop-hidden-measurement-configuration`, `stop-disallowed-positive-result`; XML и safety scan. Sharp PNG всех SVG осмотрены на 375 px: матрица, область неопределённости и hand-off loop читаемы и не обрезаются. Строгая проверка, включающая code и исключающая source lists, дала ноль совпадающих абзацев от 160 символов и ноль общих 12-словных фрагментов во всех трёх парах. + +В registry добавлены только три ноябрьские ревизии. Пользовательские application-файлы, `articles.json`, production queue и остальная незакоммиченная работа не включены в приёмку. diff --git a/web/data/editorial-revisions.mjs b/web/data/editorial-revisions.mjs index 9db509b..45ff54a 100644 --- a/web/data/editorial-revisions.mjs +++ b/web/data/editorial-revisions.mjs @@ -101,6 +101,7 @@ import { revisions as july2026Revisions } from '../scripts/upgrade-2026-07.mjs'; import { revisions as august2026Revisions } from '../scripts/upgrade-2026-08.mjs'; import { revisions as september2026Revisions } from '../scripts/upgrade-2026-09.mjs'; import { revisions as october2026Revisions } from '../scripts/upgrade-2026-10.mjs'; +import { revisions as november2026Revisions } from '../scripts/upgrade-2026-11.mjs'; // This layer replaces archived source entries without losing their stable slug and date. export const editorialRevisions = [ @@ -207,4 +208,5 @@ export const editorialRevisions = [ ...august2026Revisions, ...september2026Revisions, ...october2026Revisions, + ...november2026Revisions, ]; diff --git a/web/public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg b/web/public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg new file mode 100644 index 0000000..7f814b6 --- /dev/null +++ b/web/public/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg @@ -0,0 +1,10 @@ + + Петля безопасной передачи evidenceКарточка плана на ноябрь 2026 с критериями и открытой конфигурацией передаётся будущему владельцу evidence. Четыре условия возвращают её со статусом stop. + + Synthetic evidence hand-offплан 2026-11 · cutoff 2026-07-31 · productionEffect: not-attempted + Карточка планаcriteria + weightsliteral configurationnot-run / not-estimated + Будущий владелецполучает вопрос,не результат ине действие + synthetic-plan-hand-off + STOP возвращает карточкунедата · нет веса · скрыт protocol · заявлен winnerисправить границу, а не придумать benchmark + + diff --git a/web/public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg b/web/public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg new file mode 100644 index 0000000..e03b17a --- /dev/null +++ b/web/public/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg @@ -0,0 +1,9 @@ + + Граница компромиссов без точки победителяТри оси описывают пригодность, стоимость внедрения и эксплуатационный вопрос. В центре находится область неопределённости без результата benchmark. + Trade-off frontier: результат отсутствуетсценарий 2026-11 · source cutoff 2026-07-31 + + областьнеопределённости + ПригодностьCost of adoptionЭксплуатация + STOP: hidden configнет protocol → нет вывода + Weights — предпочтения; not-run — не benchmark; победитель не указан. + diff --git a/web/public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg b/web/public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg new file mode 100644 index 0000000..bc8a445 --- /dev/null +++ b/web/public/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg @@ -0,0 +1,12 @@ + + Плановая взвешенная матрица выбора технологииТри критерия с весами: пригодность 40, стоимость внедрения 35 и эксплуатация 25. Справа показаны условия остановки. + + План 2026-11: матрица без победителяsource cutoff 2026-07-31 · только вопросы и веса + + Критерий и весSTOP + Пригодность к задаче40Какую границу задачи уточнить? + Cost of adoption35Обучение · миграция · поддержка + Эксплуатационный вопрос25Какой сигнал когда-нибудь нужен? + Сумма весов: 100 · scores отсутствуют + Остановить, если:нет даты плананет критерия / весаскрыт protocolзаявлен winnerНе benchmark. Не rollout. + diff --git a/web/scripts/upgrade-2026-11.mjs b/web/scripts/upgrade-2026-11.mjs new file mode 100644 index 0000000..38f3653 --- /dev/null +++ b/web/scripts/upgrade-2026-11.mjs @@ -0,0 +1,208 @@ +function escapeHtml(value) { return String(value).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", '''); } +const p = (text) => '

' + text + '

'; +const h2 = (text) => '

' + text + '

'; +const code = (text) => '
' + escapeHtml(text) + '
'; +const ol = (items) => '
    ' + items.map((item) => '
  1. ' + item + '
  2. ').join('') + '
'; +const figure = (src, alt, caption) => '
' + alt + '
' + caption + '
'; +const table = (caption, headers, rows) => '
' + headers.map((cell) => '').join('') + '' + rows.map((row) => '' + row.map((cell) => '').join('') + '').join('') + '
' + caption + '
' + cell + '
' + cell + '
'; + +function cloneFixed(value) { return JSON.parse(JSON.stringify(value)); } +function deepFreeze(value) { if (value && typeof value === 'object' && !Object.isFrozen(value)) { Object.values(value).forEach(deepFreeze); Object.freeze(value); } return value; } +function plainText(html) { return html.replace(/<[^>]+>/g, ' ').replace(/&(?:quot|amp|lt|gt|#039);/g, ' ').replace(/\s+/g, ' ').trim(); } +function bodyText(html) { return plainText(html.replace(/

Проверяемые источники<\/h2>[\s\S]*?(?=

|$)/, '')); } + +const REFERENCES = deepFreeze({ + nistRisk: { title: 'NIST SP 800-30 Rev. 1: Guide for Conducting Risk Assessments', url: 'https://doi.org/10.6028/NIST.SP.800-30r1', version: 'Revision 1, September 2012, DOI 10.6028/NIST.SP.800-30r1' }, + rfc2119: { title: 'RFC 2119: Key words for use in RFCs to Indicate Requirement Levels', url: 'https://www.rfc-editor.org/rfc/rfc2119', version: 'BCP 14, March 1997, DOI 10.17487/RFC2119' }, + rfc8174: { title: 'RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words', url: 'https://www.rfc-editor.org/rfc/rfc8174', version: 'BCP 14, May 2017, DOI 10.17487/RFC8174' }, +}); +function sources(entries) { return ''; } + +const FIXED_EVALUATION_CASES = deepFreeze({ + 'weighted-plan-v1': { + id: 'weighted-plan-v1', planDate: '2026-11', sourceCutoff: '2026-07-31', + alternatives: ['fixed-runtime-a', 'fixed-runtime-b'], + criteria: [ + { id: 'fit', weight: 40, scale: '0..3', note: 'named-fit-question' }, + { id: 'cost-of-adoption', weight: 35, scale: '0..3', note: 'named-training-and-migration-question' }, + { id: 'operability', weight: 25, scale: '0..3', note: 'named-operation-question' }, + ], + measurement: { protocol: 'synthetic-configuration-v1', inputs: 'fixed-in-memory-literals', repetitions: 'not-run', uncertainty: 'not-estimated', hiddenConfiguration: false }, + requestedConclusion: 'synthetic-plan-hand-off', boundary: 'Fixed in-memory planning literal. No benchmark, choice, rollout, measurement, network, browser, file, environment, clock, secret, telemetry, external tool, production system or customer data is read or changed.', + }, + 'undated-plan-v1': { + id: 'undated-plan-v1', planDate: '', sourceCutoff: '2026-07-31', alternatives: ['fixed-runtime-a', 'fixed-runtime-b'], + criteria: [{ id: 'fit', weight: 100, scale: '0..3', note: 'named-fit-question' }], measurement: { protocol: 'synthetic-configuration-v1', inputs: 'fixed-in-memory-literals', repetitions: 'not-run', uncertainty: 'not-estimated', hiddenConfiguration: false }, requestedConclusion: 'synthetic-plan-hand-off', boundary: 'Negative fixed literal only.', + }, + 'missing-weight-v1': { + id: 'missing-weight-v1', planDate: '2026-11', sourceCutoff: '2026-07-31', alternatives: ['fixed-runtime-a', 'fixed-runtime-b'], + criteria: [{ id: 'fit', weight: 40, scale: '0..3', note: 'named-fit-question' }, { id: 'cost-of-adoption', scale: '0..3', note: 'named-training-and-migration-question' }], measurement: { protocol: 'synthetic-configuration-v1', inputs: 'fixed-in-memory-literals', repetitions: 'not-run', uncertainty: 'not-estimated', hiddenConfiguration: false }, requestedConclusion: 'synthetic-plan-hand-off', boundary: 'Negative fixed literal only.', + }, + 'hidden-measurement-v1': { + id: 'hidden-measurement-v1', planDate: '2026-11', sourceCutoff: '2026-07-31', alternatives: ['fixed-runtime-a', 'fixed-runtime-b'], + criteria: [{ id: 'fit', weight: 50, scale: '0..3', note: 'named-fit-question' }, { id: 'cost-of-adoption', weight: 50, scale: '0..3', note: 'named-training-and-migration-question' }], measurement: { protocol: '', inputs: 'fixed-in-memory-literals', repetitions: 'not-run', uncertainty: 'not-estimated', hiddenConfiguration: true }, requestedConclusion: 'synthetic-plan-hand-off', boundary: 'Negative fixed literal only.', + }, + 'claimed-benchmark-win-v1': { + id: 'claimed-benchmark-win-v1', planDate: '2026-11', sourceCutoff: '2026-07-31', alternatives: ['fixed-runtime-a', 'fixed-runtime-b'], + criteria: [{ id: 'fit', weight: 50, scale: '0..3', note: 'named-fit-question' }, { id: 'cost-of-adoption', weight: 50, scale: '0..3', note: 'named-training-and-migration-question' }], measurement: { protocol: 'synthetic-configuration-v1', inputs: 'fixed-in-memory-literals', repetitions: 'not-run', uncertainty: 'not-estimated', hiddenConfiguration: false }, requestedConclusion: 'benchmark-winner-selected', boundary: 'Negative fixed literal only.', + }, +}); + +export function createFixedTechnologyEvaluationCase(id = 'weighted-plan-v1') { const value = FIXED_EVALUATION_CASES[id]; return value ? deepFreeze(cloneFixed(value)) : undefined; } +function stop(status, reason, nextAction) { return deepFreeze({ status, reason, nextAction, productionEffect: 'not-attempted' }); } +export function assessFixedTechnologyEvaluation(input) { + if (!Object.values(FIXED_EVALUATION_CASES).some((item) => JSON.stringify(item) === JSON.stringify(input))) return stop('stop-unknown-fixed-input', 'input-is-not-a-known-named-fixed-literal', 'select-a-named-fixed-case'); + if (input.planDate !== '2026-11' || input.sourceCutoff !== '2026-07-31') return stop('stop-undated-plan-or-cutoff', 'november-plan-and-july-cutoff-are-required', 'name-2026-11-and-2026-07-31'); + if (!Array.isArray(input.criteria) || input.criteria.length < 2 || input.criteria.some((criterion) => !criterion.id || !Number.isFinite(criterion.weight) || !criterion.scale || !criterion.note)) return stop('stop-missing-criterion-or-weight', 'every-criterion-needs-id-weight-scale-and-question', 'complete-the-fixed-criteria'); + if (input.criteria.reduce((sum, criterion) => sum + criterion.weight, 0) !== 100) return stop('stop-invalid-weight-total', 'fixed-weights-must-total-100', 'rebalance-named-weights'); + if (!input.measurement?.protocol || input.measurement.hiddenConfiguration || input.measurement.inputs !== 'fixed-in-memory-literals' || input.measurement.repetitions !== 'not-run' || input.measurement.uncertainty !== 'not-estimated') return stop('stop-hidden-measurement-configuration', 'configuration-must-be-literal-and-not-run', 'name-the-synthetic-configuration'); + if (input.requestedConclusion !== 'synthetic-plan-hand-off') return stop('stop-disallowed-positive-result', 'benchmark-choice-rollout-or-measurement-result-is-forbidden', 'use-synthetic-plan-hand-off'); + return deepFreeze({ status: 'synthetic-plan-hand-off', caseId: input.id, criteria: deepFreeze(cloneFixed(input.criteria)), measurement: deepFreeze(cloneFixed(input.measurement)), boundary: input.boundary, productionEffect: 'not-attempted', nextAction: 'give-the-fixed-plan-to-a-future-evidence-owner' }); +} +export function runFixedTechnologyEvaluationFixture() { + const expected = [['weighted-plan-v1', 'synthetic-plan-hand-off'], ['undated-plan-v1', 'stop-undated-plan-or-cutoff'], ['missing-weight-v1', 'stop-missing-criterion-or-weight'], ['hidden-measurement-v1', 'stop-hidden-measurement-configuration'], ['claimed-benchmark-win-v1', 'stop-disallowed-positive-result']]; + const checks = expected.map(([id, status]) => ({ id, expected: status, actual: assessFixedTechnologyEvaluation(createFixedTechnologyEvaluationCase(id)).status })); + return deepFreeze({ passed: checks.filter((check) => check.expected === check.actual).length, total: checks.length, accepted: checks.every((check) => check.expected === check.actual) && Object.isFrozen(createFixedTechnologyEvaluationCase()), checks: deepFreeze(checks) }); +} + +function revision(meta, parts, referenceEntries) { + const contentHtml = parts.join('') + h2('Проверяемые источники') + sources(referenceEntries); + const proseLength = bodyText(contentHtml).length; + if (proseLength < 5000 || proseLength > 15000) throw new Error(meta.slug + ': body length ' + proseLength); + return deepFreeze({ ...meta, contentHtml, proseLength }); +} + +const practice = revision({ + slug: 'editorial-2026-11-practice-technology-evaluation', title: 'Сравнение технологий без хайпа: практический маршрут', categories: ['Инженерные практики', 'Архитектура'], cover: '/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg', excerpt: 'План на ноябрь 2026: как собрать взвешенную матрицу с ценой внедрения и не выдать подготовку за выбор технологии.', readingMinutes: 18, +}, [ + p('План на ноябрь 2026 с source cutoff 31.07.2026 начинается не с названия технологии, а с конкретной поломки выбора: команда сравнивает две альтернативы по одному удобному демо, а стоимость внедрения остаётся в сноске. Через месяц всплывают обучение, миграция, поддержка и обратимость. Цена ошибки измеряется не красивой таблицей, а временем людей на разбор неучтённой работы и зависимостей, которые уже успели стать обязательными.'), + p('Вторая проблема — матрица, в которой заранее известен победитель. Вес «скорости» получают после просмотра результата, критерий совместимости описывают туманно, а отсутствующие данные заменяют числом. Цена такой точности — ложный мандат на выбор: спор переносится из явных допущений в необратимую реализацию. Это не отчёт, не benchmark и не rollout. Ни один замер или выбор не происходил: ниже только сценарий на 2026-11, а положительный output literal ограничен synthetic-plan-hand-off с productionEffect: not-attempted.'), + h2('Сначала формулируют решение, которое матрица не может принять'), + p('Матрица полезна, когда от неё не требуют магии. Она не выбирает стек и не превращает оценку в доказательство. Её работа уже: удержать на одном листе вопрос, альтернативы, критерии, вес и цену допущения. Для планового примера есть только fixed-runtime-a и fixed-runtime-b. Эти имена нарочно не соответствуют продуктам. Они не обещают, что одна технология реально существует, совместима с вашим кодом или будет использоваться в ноябре.'), + p('Хороший вопрос имеет границу. Не «что современнее?», а «какая из двух альтернатив требует меньшей работы адаптации при явно названном способе обучения и миграции?». Не «что быстрее?», а «какой сигнал мог бы в будущем ответить на конкретную операционную гипотезу?». Пока сигнала нет, в клетке не должно быть выдуманного балла. В плановой матрице вес — это публичное предпочтение вопроса, не вероятность и не прогноз результата.'), + figure('/assets/editorial/2026/technology-evaluation-2026-weighted-decision-matrix.svg', 'Взвешенная матрица планового сравнения: пригодность 40, стоимость внедрения 35 и эксплуатация 25; справа показан stop при отсутствующем весе или скрытой конфигурации.', 'Оригинальная схема для сценария ноября 2026. Она показывает структуру вопросов и stop-условия, но не содержит баллов, победителя, benchmark или внедрения.'), + table('Плановая матрица без результатов', ['Критерий', 'Вес', 'Что будет уточняться', 'Что не разрешено утверждать'], [ + ['Пригодность к задаче', '40', 'какая граница задачи названа?', 'что вариант уже решает задачу'], + ['Cost of adoption', '35', 'какие обучение, миграция и поддержка будут считаться?', 'что переход дешёв или завершён'], + ['Эксплуатация', '25', 'какой будущий операционный вопрос важен?', 'что наблюдалась надёжность'], + ['Итого', '100', 'почему именно эти приоритеты?', 'что сумма выбрала победителя'], + ]), + h2('Cost of adoption — отдельный критерий, а не штраф в комментарии'), + p('Стоимость внедрения часто прячут в слове «экосистема». Это плохой контейнер: в нём смешиваются навыки, миграция, документация, инструменты, поддержка и цена ошибки при возврате. В сценарии каждый из этих пунктов остаётся вопросом, пока не назван владелец будущего evidence. Такой подход не раздувает таблицу до финансовой модели. Он не даёт технической демонстрации незаметно отменить работу по переходу.'), + p('Для cost of adoption стоит записать не один балл, а состав: чему должна научиться группа; какие границы кода или процессов изменятся; кто будет поддерживать редкий случай; как обнаружить, что обратимость утрачена. Это не означает, что все пункты равны. Вес 35 показывает лишь предварительный приоритет данной synthetic конфигурации. В другой задаче его можно изменить, но тогда изменение надо объяснить до сравнения, а не после удобного вывода.'), + h2('Вес выражает ценность вопроса, не силу факта'), + p('Веса 40, 35 и 25 в примере суммируются до 100, чтобы пропуск был механически заметен. Это не научная шкала и не усреднение мнений. Если критерий не назван или у него нет веса, функция останавливается. Fail-closed статус лучше декоративного нуля: ноль мог бы означать плохой результат, хотя на самом деле неизвестно, что именно хотели оценить. Отдельно важно не делать вес результатом ожидаемого выигрыша: тогда модель уже содержит свой желаемый ответ.'), + p('Нужна и короткая запись причины веса. Для пригодности — потому что без неё сравнение не относится к задаче. Для adoption — потому что переход создаёт работу за пределами исходного прототипа. Для эксплуатации — потому что будущему владельцу надо знать, какой вопрос наблюдения вообще имеет смысл. Эти формулировки — собственная модель редакции, не вывод NIST или RFC. Внешние источники здесь помогают отделять требования и оценку риска от риторики, но не подтверждают наш порядок чисел.'), + h2('Literal validator матрицы'), + code("import { createFixedTechnologyEvaluationCase, assessFixedTechnologyEvaluation } from './upgrade-2026-11.mjs';\n\nconst plan = createFixedTechnologyEvaluationCase('weighted-plan-v1');\nconst result = assessFixedTechnologyEvaluation(plan);\nconsole.log({ status: result.status, effect: result.productionEffect, next: result.nextAction });\n// { status: 'synthetic-plan-hand-off', effect: 'not-attempted', next: 'give-the-fixed-plan-to-a-future-evidence-owner' }"), + p('Пример буквально запускается только с экспортами этого модуля и named in-memory literal. JSON clone отделяет вход от внутренней константы, deep freeze блокирует изменение вложенных полей в процессе. Он не читает сеть, браузер, файлы, env, часы, секреты, telemetry или внешние инструменты; он не делает benchmark и не запускает production-код. Его положительный ответ — безопасная передача конфигурации будущему владельцу evidence, а не рекомендация, выбор или разрешение на rollout.'), + h2('Порядок подготовки ноябрьского сравнения'), + ol(['Пометить документ как план/сценарий на 2026-11 и рядом записать source cutoff 2026-07-31.', 'Назвать одну границу решения и две обезличенные альтернативы, не добавляя историю их использования.', 'Выписать пригодность, cost of adoption и эксплуатационный вопрос; для каждого дать scale и вес.', 'Проверить, что веса дают 100, а у каждого пункта есть причина и будущий владелец evidence.', 'Описать synthetic configuration буквально: только fixed literals, без скрытых параметров и запусков.', 'Запустить fixture и передать только synthetic-plan-hand-off; не писать winner, benchmark, rollout или measured.']), + h2('Матрица не должна стирать разницу между неизвестным и плохим'), + p('У оценочной формы есть удобная, но опасная привычка: пустую клетку закрашивают нулём, чтобы таблица «сошлась». Это подменяет два разных состояния. Ноль может означать, что вариант явно не подходит по заранее понятной причине. Пустота означает другое: критерий, способ проверки или данные для него пока не названы. В первом случае можно обсуждать отрицательное свойство альтернативы. Во втором нужно вернуть вопрос владельцу формулировки. Если смешать их, матрица штрафует не технологию, а собственное незнание команды.'), + p('Для ноябрьского плана это особенно существенно, потому что никаких фактических scores нет. Не надо назначать фиксированным именам fixed-runtime-a и fixed-runtime-b даже условные победы ради учебного эффекта. Они существуют только как две позиции, для которых будущий evidence contract мог бы задать одинаково понятный вопрос. Такой минимализм не делает статью менее практичной: он сохраняет момент, когда настоящий спор ещё можно вести об условиях, а не о красивой сумме в последней колонке.'), + h2('Опасные сокращения, которые матрица должна остановить'), + p('Первый опасный случай — недатированный plan. Без ноября и cutoff текст легко начинает звучать как уже состоявшееся решение. Второй — отсутствующий критерий или вес. Тогда читатель не отличит сознательное исключение от забытой стоимости. Третий — скрытая конфигурация измерения: фраза «потом прогоняем одинаково» ничего не говорит о входах, повторениях или неопределённости. Четвёртый — положительный результат вида «победитель выбран». Для будущего сценария он запрещён даже тогда, когда таблица выглядит аккуратно.'), + p('В validator эти четыре сокращения имеют отдельные stop-коды. Это не policy настоящей команды и не security control. Это учебная граница, которая не позволяет тексту перепрыгнуть от вопросов к факту. Если реальному владельцу понадобится сравнение, он в отдельном процессе должен определить разрешённые данные, среду, метод и полномочия. Ноябрьский пакет ничего из этого не предполагает и не создаёт.'), + h2('Ограничения и следующий шаг'), + p('Взвешенная матрица не оценивает зрелость людей, лицензии, юридические условия, доступность, безопасность или реальные затраты. Она также не показывает, как объединять конфликтующие сигналы и когда один стоп должен отменять весь выбор. NIST SP 800-30 говорит о структуре risk assessment, но не выдаёт веса для конкретной технологии. RFC 2119 объясняет контекст требований, а не делает наш критерий обязательным.'), + p('Следующий шаг в будущем сценарии — назначить владельца, который сможет предложить разрешённый evidence contract для одного критерия. До этого надо оставить ячейки без баллов и hand-off без победителя. Если будущий контракт не может назвать входы и границу интерпретации, проблему следует вернуть к формулировке задачи, а не компенсировать уверенностью в презентации.'), + h2('Проверяемые источники'), +], [ + { key: 'nistRisk', use: 'Первичный источник использован для различения структуры оценки риска и конкретного проектного решения.', boundary: 'Не задаёт наши веса, альтернативы, результат benchmark или выбор.' }, + { key: 'rfc2119', use: 'Первичный RFC использован только для аккуратного смысла слов требования.', boundary: 'Не создаёт обязательной политики матрицы и не подтверждает внедрение.' }, + { key: 'rfc8174', use: 'Первичный RFC уточняет контекст терминов BCP 14.', boundary: 'Не превращает synthetic status в одобрение или факт эксплуатации.' }, +]); + +const mechanism = revision({ + slug: 'editorial-2026-11-mechanism-technology-evaluation', title: 'Сравнение технологий без хайпа: как принять инженерное решение', categories: ['Архитектура', 'Инженерные практики'], cover: '/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg', excerpt: 'Сценарий ноября 2026: как отделить измерение, неопределённость и веса от псевдоточного выбора технологии.', readingMinutes: 19, +}, [ + p('В планах сравнения технологий часто ломается сам механизм рассуждения: два неполных наблюдения переводят в число с двумя знаками после запятой, а затем число называют выбором. Проблема конкретна — неизвестность входа исчезает из записи. Цена ошибки появляется позже: команда оптимизирует не ту нагрузку, объясняет расхождение «шумом» и уже не может восстановить, какие параметры были приняты молча.'), + p('Вторая проблема — смешать измерение и предпочтение. Вес критерия начинают выдавать за факт о мире, а условный measurement — за benchmark, который будто бы уже прошёл. Цена такого смешения — невозможность оспорить решение по частям: несогласный вынужден спорить с авторитетным «итогом», а не с конфигурацией. Ноябрь 2026 ещё не наступил; здесь нет field report, замера, выбора, rollout или результата. Source cutoff равен 2026-07-31, а единственный положительный выход кода — synthetic-plan-hand-off, productionEffect: not-attempted.'), + h2('Механизм начинается с разделения четырёх сущностей'), + p('В этом сценарии есть четыре независимые сущности: вопрос, наблюдение, неопределённость и предпочтение. Вопрос фиксирует, что хотели бы узнать в будущем. Наблюдение — то, что отдельный разрешённый процесс когда-нибудь мог бы получить. Неопределённость описывает, что мешает переносить сигнал на другую границу. Предпочтение — вес, показывающий, почему вопрос важен для решения. Ни одна сущность не заменяет другую. Особенно опасно подставить weight вместо evidence: это делает решение гладким, но не проверяемым.'), + p('Fixed literal намеренно сообщает repetitions: not-run и uncertainty: not-estimated. Это не пустая деталь. Она запрещает придумать среднее, разброс или победителя там, где не было запуска. Запись «не оценено» полезнее псевдоточности: она оставляет будущему исследованию место назвать вариацию нагрузки, версию окружения, границу времени и способ сравнения. В текущем пакете этих сущностей нет, потому что их нельзя честно взять из памяти без внешнего наблюдения.'), + figure('/assets/editorial/2026/technology-evaluation-2026-tradeoff-frontier.svg', 'Схема границы trade-off: пригодность, стоимость внедрения и эксплуатационный вопрос образуют три оси, но точка выбора отсутствует; вместо неё показана область неопределённости и stop при неявной конфигурации.', 'Оригинальная схема механизма на ноябрь 2026: она отделяет вопросы и предпочтения от несуществующего результата измерения.'), + table('Разбор механизма будущего сравнения', ['Сущность', 'Что допускается записать сейчас', 'Что требует будущего evidence', 'Ошибочная подмена'], [ + ['Вопрос', 'граница задачи и named alternative', 'релевантность к реальной системе', 'назвать вопрос результатом'], + ['Конфигурация', 'fixed literals и запрет запуска', 'входы, среда, повторения', 'спрятать параметры в «равных условиях»'], + ['Неопределённость', 'not-estimated', 'разброс и переносимость сигнала', 'поставить точный балл'], + ['Вес', 'явный приоритет 40/35/25', 'согласование владельцев решения', 'выдать приоритет за факт'], + ]), + h2('Измерение не начинается с числа'), + p('До любого числа нужно назвать наблюдаемую величину и её границу. «Производительность» не годится: это контейнер для несовместимых вопросов. Будущая конфигурация могла бы спросить о задержке одного известного действия, объёме памяти при заданной форме данных или стоимости восстановления после ошибки. Но даже так она не становится benchmark, пока не обозначены вход, способ запуска, повторения, правило исключения и трактовка вариации. В ноябрьской модели эти поля сознательно не заполняются внешними значениями.'), + p('Нельзя исправить отсутствие конфигурации обещанием честности. Слова «на одинаковой машине» или «в реалистичной нагрузке» скрывают больше, чем сообщают: какая версия, какой вход, какие зависимости, какое ограничение времени, какой порядок прогонов? Для synthetic plan корректнее буквальная строка synthetic-configuration-v1 и явный признак not-run. Она не похожа на реальную методику, и это достоинство: читатель не примет её за свидетельство проведённого опыта.'), + h2('Неопределённость — свойство вывода, а не неудобная сноска'), + p('Даже будущий повторяемый запуск не отменит неопределённость. Сигнал может зависеть от входа, версии, выбранного измерителя и того, какую цену команда готова считать существенной. Поэтому корректный механизм не строит одну магическую ось «лучше». Он держит frontier: несколько свойств могут улучшаться или ухудшаться одновременно, а выбор требует явно сказать, чем готовы пожертвовать. На диаграмме нет точки-победителя именно потому, что такой точки не создал ни один запуск.'), + p('Псевдоточность появляется и в нормализации. Если шкала 0..3 введена ради удобства обсуждения, она не даёт отношение «три в полтора раза лучше двух». Это ordinal ярлык вопроса. Складывать такие оценки можно только как проектное соглашение с ограничением: итог упорядочивает подготовленные гипотезы, но не доказывает реальный эффект. В этом пакете даже такого итога нет. Мы оставляем лишь конструкцию, в которой missing scale, weight или uncertainty mode останавливают передачу.'), + h2('Weights нужно подвергать отдельному сомнению'), + p('Веса в 40, 35 и 25 не претендуют на объективность. Они полезны только потому, что открыты для спора до появления результата. Человек может спросить: почему cost of adoption важнее эксплуатации в этой плановой задаче? Ответ не должен быть «потому что матрица так посчитала». Нужно вернуться к ценности границы: сколько скрытой работы создаёт переход, кому она достанется и почему это свойство сейчас существеннее другого. Если причины нет, у веса нет права маскироваться под метрику.'), + p('Полезная проверка — изменить один вес мысленно и посмотреть, меняется ли сам вопрос. Если изменение не влияет на рассуждение, число декоративно. Если оно меняет всё без объяснения, модель слишком хрупка. Но это упражнение не заменяет sensitivity analysis и тем более не доказывает устойчивость выбора. Для проведения такого анализа нужны реальные разрешённые значения и отдельная методика. Ноябрьский literal не получает их, поэтому отдаёт hand-off, а не таблицу побед.'), + h2('Исполнимый stop для скрытого measurement'), + code("import { createFixedTechnologyEvaluationCase, assessFixedTechnologyEvaluation } from './upgrade-2026-11.mjs';\n\nconst plan = createFixedTechnologyEvaluationCase('hidden-measurement-v1');\nconst result = assessFixedTechnologyEvaluation(plan);\nconsole.log({ status: result.status, reason: result.reason, effect: result.productionEffect });\n// { status: 'stop-hidden-measurement-configuration', reason: 'configuration-must-be-literal-and-not-run', effect: 'not-attempted' }"), + p('Snippet не имитирует запрос, таймер или benchmark harness. Он создаёт JSON-cloned frozen case в памяти и проверяет, что protocol пуст, а hiddenConfiguration включён. Поэтому выдаётся детерминированный stop. Нет чтения файлов, env, сети, browser state, часов, secrets, telemetry или внешних инструментов. Отсутствие результата — не failure выполнения: это намеренное свойство механизма, которое защищает от ложной истории измерения.'), + h2('Порядок для будущей измерительной конфигурации'), + ol(['Написать проблему и цену без названия «самой быстрой» альтернативы.', 'Отделить наблюдаемую величину от того, почему она важна для решения.', 'Назвать inputs, protocol, repetitions и uncertainty rule в отдельном будущем contract; не брать их из неявной среды.', 'Сохранить missing information как unknown, а не как нулевой балл.', 'Зафиксировать веса как спорные предпочтения и проверить сумму 100.', 'Если конфигурация скрыта или вывод обещает winner, вернуть stop и передать только план владельцу evidence.']), + h2('Где механизм сознательно не помогает'), + p('Он не решает, что считать допустимой нагрузкой, кто имеет право запускать код, какие данные можно использовать и как согласовать риск с продуктом. Он не воспроизводит окружение и не учит статистике. Название uncertainty не превращает неопределённость в оценённый интервал; отсутствие скрытой конфигурации не делает конфигурацию хорошей. Эти пределы важны, потому что иначе статья выглядела бы как руководство к benchmark, хотя она описывает только честное состояние до benchmark.'), + p('NIST SP 800-30 даёт язык для разговора о риске и источниках неопределённости, но не говорит, какой runtime выбрать. RFC 2119 и RFC 8174 объясняют силу нормативных слов, но не подтверждают, что у этой команды есть одинаковые критерии. Факт источника отделён от собственной модели: frontier, веса, шкала и fail-closed statuses — редакционная конструкция, привязанная к плану 2026-11.'), + h2('Ограничения и следующий шаг'), + p('Следующий шаг — не запуск, а создание отдельного evidence contract: владелец будущего исследования должен записать разрешённые входы, метод, условия остановки, диапазон интерпретации и способ сообщить неопределённость. Если он не может этого сделать, сравнение пока не созрело для измерения. Нельзя заменить этот пробел собственным benchmark: в текущем сценарии такой benchmark не происходил и не может быть назван фактом.'), + p('После появления отдельного разрешённого контракта всё равно придётся обсуждать trade-off, а не искать универсального лидера. Только затем кто-то с полномочиями сможет принять решение. Эта статья не предсказывает, каким оно будет, и не говорит, что ноябрьский rollout состоится. Её результат скромнее: она оставляет вопрос измеримым, а неизвестность видимой.'), + h2('Проверяемые источники'), +], [ + { key: 'nistRisk', use: 'Первичный источник использован для терминов о неопределённости и оценке риска.', boundary: 'Не подтверждает measurement, benchmark, frontier или выбор технологии.' }, + { key: 'rfc2119', use: 'Первичный RFC использован для границы нормативной формулировки.', boundary: 'Не задаёт метод измерения и не превращает план в факт.' }, + { key: 'rfc8174', use: 'Первичный RFC использован как уточнение BCP 14.', boundary: 'Не назначает веса и не подтверждает результат будущего сценария.' }, +]); + +const field = revision({ + slug: 'editorial-2026-11-field-technology-evaluation', title: 'Сравнение технологий без хайпа: кейс с ограничениями и выводами', categories: ['Инженерные практики', 'Процессы'], cover: '/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg', excerpt: 'Плановый field-сценарий на ноябрь 2026: безопасный synthetic evidence hand-off вместо выдуманного отчёта о сравнении.', readingMinutes: 18, +}, [ + p('У будущего сравнения технологий есть типичный организационный сбой: человек передаёт следующему владельцу фразу «вариант B доказал преимущество», хотя у него есть лишь незакрытые вопросы. Конкретная проблема — hand-off переносит уверенный вывод вместо границы evidence. Цена ошибки высока: получатель тратит время на защиту несуществующего benchmark, а решение получает историю, которую невозможно проверить или отменить без потери лица.'), + p('Вторая проблема — назвать synthetic пример field report. Тогда фиксированные строки, придуманные для разговора, выглядят как данные среды, а stop воспринимается как мелкая формальность. Цена — ложная операционная память: кто-то позже решит, что выбор, rollout или замер уже случились. Ничего такого не происходило. Ноябрь 2026 ещё впереди, source cutoff — 2026-07-31; это план/сценарий, не кейс о внедрении. Положительная ветка кода возвращает только synthetic-plan-hand-off и всегда содержит productionEffect: not-attempted.'), + h2('Field здесь означает форму передачи, а не наблюдение поля'), + p('Слово field обычно обещает опыт: контекст, временную линию, наблюдение и последствия. В этом пакете оно употребляется уже. Мы рассматриваем, как подготовить карточку для будущего владельца evidence, не выдумав ни одной строки истории. Карточка хранит дату плана, cutoff источников, alternatives, criteria, visible measurement configuration, статус и следующий вопрос. Она не хранит ID проекта, клиентов, инфраструктуру, реальные метрики, ссылки на dashboard или утверждение о совместимости.'), + p('Такой hand-off полезен именно своей неполнотой. Получатель сразу видит, чего нет: результата запуска, оценки uncertainty, полномочий на выбор и доказательства cost of adoption. Он не обязан раскапывать прозаический рассказ, чтобы обнаружить пробел. Взамен карточка даёт точный адрес следующего действия: владелец будущего evidence должен сформировать отдельный разрешённый contract. Это больше похоже на передачу вопроса, чем на передачу решения, и этим снижает цену неверной эскалации.'), + figure('/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg', 'Петля synthetic evidence hand-off: план на 2026-11 с cutoff 2026-07-31 передаёт named criteria и открытую конфигурацию владельцу будущего evidence; stop возвращает карточку при недате, отсутствующем весе, скрытом measurement или заявленном результате.', 'Оригинальная схема safe hand-off. Стрелки показывают порядок вопросов; они не изображают реально проведённый benchmark, выбор или rollout.'), + table('Содержимое безопасной карточки передачи', ['Поле', 'Что в нём допустимо', 'Чего в нём нет', 'Кому адресован следующий шаг'], [ + ['Временная граница', '2026-11 и cutoff 2026-07-31', 'прошедшая дата внедрения', 'редактору планового сценария'], + ['Criteria', 'named question, scale, weight', 'заполненный score или лидер', 'владельцу decision framing'], + ['Measurement', 'fixed literal, not-run, not-estimated', 'trace, timing, telemetry', 'владельцу будущего evidence'], + ['Output', 'synthetic-plan-hand-off либо stop', 'approve, select, deploy, measured', 'следующему reviewer'], + ]), + h2('Передача начинается с provenance, а не с вывода'), + p('Provenance в этом сценарии не означает журнал событий. Это короткая подпись происхождения: fixed in-memory literal, JSON clone, deep freeze, дата плана и cutoff. Её задача — дать читателю повод не доверять карточке как отчёту. Если рядом нет этой подписи, fixed-runtime-a можно принять за настоящий сервис, а not-run — за плохо записанный результат. Явная искусственность не украшение: она удерживает границу между моделью и миром.'), + p('Особенно важно сохранять source boundary. Источник, опубликованный до 31.07.2026, может объяснить термин или ограничение документа; он не сообщает, что сделает команда в ноябре. Собственная модель добавляет веса, структуру hand-off и код статусов. Эти слои нужно называть разными словами. Нельзя взять авторитет первичного RFC и на его основании сказать, что synthetic конфигурация уже прошла review. Источник даёт факт о тексте источника, модель — только правило этого учебного пакета.'), + h2('Fail-closed status делает пробел передаваемым'), + p('Четыре остановки несут разный смысл. stop-undated-plan-or-cutoff сообщает, что нельзя понять, относится ли карточка к будущему. stop-missing-criterion-or-weight показывает, что выборочное внимание скрыто. stop-hidden-measurement-configuration не разрешает назвать словом benchmark неописанный запуск. stop-disallowed-positive-result останавливает попытку передать winner или rollout как будто они следуют из preparation. Каждый статус возвращает nextAction, а не диагноз личности автора.'), + p('Fail-closed важен в hand-off потому, что получатель не должен угадывать, что исправить. Общая фраза «не хватает данных» расширяет scope до всего мира. Точный stop говорит, какое поле недопустимо и какую границу надо сделать видимой. При этом он не заменяет будущую проверку: добавленный weight ещё не делает оценку верной, а раскрытый protocol ещё не производит измерение. Карточка лишь становится честной для передачи дальше.'), + h2('Runnable example передаёт план, не действие'), + code("import { createFixedTechnologyEvaluationCase, assessFixedTechnologyEvaluation } from './upgrade-2026-11.mjs';\n\nconst plan = createFixedTechnologyEvaluationCase('claimed-benchmark-win-v1');\nconst result = assessFixedTechnologyEvaluation(plan);\nconsole.log({ status: result.status, next: result.nextAction, effect: result.productionEffect });\n// { status: 'stop-disallowed-positive-result', next: 'use-synthetic-plan-hand-off', effect: 'not-attempted' }"), + p('Этот пример намеренно содержит запрещённый вывод benchmark-winner-selected. Он не ищет benchmark output и не отменяет чей-либо выбор. Все значения зашиты как literals, clone создаётся только в памяти, freeze не позволяет подменить вложенный conclusion после factory. В процессе не открываются browser, сеть, file system, environment, clock, secrets, telemetry или внешние инструменты. Возвращённый stop доказывает только одно свойство кода: этот сценарий не пропустит позитивный результат, который не имеет права существовать.'), + h2('Как выглядит безопасный synthetic evidence hand-off'), + p('Получателю не надо писать «технология перспективна». Достаточно передать: рассматривается план 2026-11; источники не позднее cutoff; есть три named criteria с weights; measurement configuration literal и not-run; решение отсутствует; нужен владелец evidence. Такая запись не впечатляет лозунгом, зато сохраняет объект спора. Если владелец возражает, он может изменить ровно один слой: границу вопроса, набор criteria, будущий protocol или порядок ответственности.'), + p('Нельзя подменять hand-off очередью работ. Карточка не ставит задач, не вызывает сервис, не отправляет уведомление и не меняет registry. Она может сказать, что следующий владелец должен быть назван, но не может назначить человека и срок. Иначе synthetic exercise незаметно станет внешним действием. Для плановой публикации безопаснее оставить ownership как открытый вопрос, чем изобразить выполненную координацию.'), + h2('Порядок передачи для сценария ноября'), + ol(['Проверить, что карточка прямо называет 2026-11 и source cutoff 2026-07-31.', 'Показать provenance: fixed literals, JSON clone и deep freeze; не добавлять правдоподобных данных.', 'Проверить каждый criterion на id, scale, question и weight, а сумму weights — на 100.', 'Открыто записать protocol и отметки not-run/not-estimated; скрытую конфигурацию вернуть с stop.', 'Запретить слова winner, selected, benchmark complete, rollout, deploy и measured в positive branch.', 'Передать status, boundary и nextAction владельцу будущего evidence без утверждения, что работа уже началась.']), + h2('Чего field-ярлык не разрешает'), + p('Он не разрешает говорить о пользователях, времени ответа, экономии, проценте ошибок или зрелости инструмента. Он не делает источники после cutoff допустимыми и не превращает первичные документы в данные конкретной среды. Он не заменяет legal, security, accessibility или operational review. Отдельно опасно сказать «мы проведём собственный benchmark» так, будто его итог уже известен. В будущем сценарии корректная замена — synthetic configuration, которая описывает только форму ещё не созданного evidence contract.'), + p('Нельзя также считать freeze защитой от внешнего вмешательства. Это локальное свойство fixture, не безопасность production. JSON clone полезен потому, что делает пример независимым от shared reference, а не потому, что способен защитить реальную систему. Такие разграничения кажутся педантичными, пока карточка не начинает жить дольше автора. Потом именно они определяют, сможет ли новый читатель отличить ограничение кода от факта эксплуатации.'), + h2('Ограничения и следующий шаг'), + p('NIST SP 800-30 может подсказать аккуратность в разговоре о риске; RFC 2119 и RFC 8174 — дисциплину нормативных слов. Ни один из них не подтверждает выбранную matrix, существование альтернатив или будущий outcome. Редакционная модель намеренно не пытается заменить эти пробелы. Её максимальная польза — сделать недостающее evidence объектом передачи, а не скрытой причиной поспешного решения.'), + p('Следующий шаг — будущему владельцу evidence contract решить, допустимы ли реальные inputs и какой результат он вообще сможет интерпретировать. Пока такого контракта нет, честный status остаётся hand-off или stop. Не следует превращать этот draft в report задним числом: ни benchmark, ни внедрение, ни выбор в нём не произошли и не заявляются.'), + h2('Проверяемые источники'), +], [ + { key: 'nistRisk', use: 'Первичный источник использован только для различения risk vocabulary и собственного решения.', boundary: 'Не является field evidence, подтверждением benchmark или разрешением rollout.' }, + { key: 'rfc2119', use: 'Первичный RFC использован для аккуратной силы слов требования.', boundary: 'Не подтверждает status карточки и не создаёт workflow команды.' }, + { key: 'rfc8174', use: 'Первичный RFC уточняет использование BCP 14.', boundary: 'Не разрешает называть synthetic hand-off фактом выбора.' }, +]); + +export const revisions = deepFreeze([practice, mechanism, field]); +export function verifyRevisionsAgainstFixture() { + const fixture = runFixedTechnologyEvaluationFixture(); + const articleChecks = revisions.map((revisionItem) => { const text = bodyText(revisionItem.contentHtml); return text.length >= 5000 && text.length <= 15000 && /(цен[аы]|стоимост|издержк)/i.test(text.slice(0, 1000)) && //.test(revisionItem.contentHtml) && /
/.test(revisionItem.contentHtml) && /
/.test(revisionItem.contentHtml) && /
    /.test(revisionItem.contentHtml); }); + return deepFreeze({ passed: fixture.passed + articleChecks.filter(Boolean).length, total: fixture.total + articleChecks.length, accepted: fixture.accepted && articleChecks.every(Boolean), fixture, articleChecks, characters: Object.fromEntries(revisions.map((revisionItem) => [revisionItem.slug, bodyText(revisionItem.contentHtml).length])) }); +} +if (process.argv.includes('--verify-fixture')) { const result = verifyRevisionsAgainstFixture(); process.stdout.write(JSON.stringify(result, null, 2) + '\n'); if (!result.accepted) process.exitCode = 1; } +if (process.argv.includes('--print-revisions')) process.stdout.write(JSON.stringify(revisions) + '\n');