revise December 2027 author manifesto plan articles
Build and deploy / deploy (push) Successful in 15s

This commit is contained in:
2026-07-31 21:02:47 +03:00
parent 6eb359ef5f
commit 3bfc3f21c4
7 changed files with 370 additions and 1 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Производство редакционных партий
На 31 июля 2026 года строгий аудит проходит 355 из 358 созданных материалов. Осталось 3 материала, которые не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить.
На 31 июля 2026 года строгий аудит проходит 358 из 358 созданных материалов. Непринятых материалов не осталось: каждая серия прошла исследовательское, редакторское и визуальное ревью, а затем отдельную публикационную проверку.
## Одна партия
+88
View File
@@ -0,0 +1,88 @@
# P118 — декабрь 2027: «Манифест инженерного письма»
## Статус и временная граница
Редакторская дата: **2026-07-31**. Декабрь 2027 ещё не наступил, поэтому P118 — только **plan/scenario** с `planDate: 2027-12` и source cutoff **2026-07-31**. Пакет не сообщает о будущих статьях, архиве, редакторе, review, feedback, corrections, публикации, author development, команде, метриках или production effect.
Модуль использует только named fixed in-memory literals. Доменный evaluator не читает filesystem, network, environment, clock, secrets, telemetry, system или data; CLI-вывод существует только для локальной проверки экспортированного JSON. Единственный accepted positive output — `synthetic-plan-hand-off`; на positive и stop-ветках `productionEffect` всегда `not-attempted`.
## Проход 1 — evidence / temporal / voice
| Статья | Body chars без sources | Независимый угол | Временная граница | Источники: применение и boundary |
| --- | ---: | --- | --- | --- |
| `editorial-2027-12-practice-author-manifesto` | 10 874 | decision record до technical advice | 2026-07-31 / 2027-12 / cutoff 2026-07-31 | RFC 2119, March 1997, immutable URL; модальные границы совета, не доказательство статьи или решения. W3C PROV-DM, 30 April 2013, dated URL; происхождение claim, не история локального архива или результата. |
| `editorial-2027-12-mechanism-author-manifesto` | 11 177 | rubric: evidence levels, claims, falsifiability и три прохода | та же | RFC 8174, May 2017, immutable URL; точное чтение требований, не review или verdict. W3C PROV-DM, 30 April 2013; различение claim и provenance, не evidence P118. |
| `editorial-2027-12-field-author-manifesto` | 10 831 | corrective hand-off без invented feedback или author history | та же | RFC 2119 и RFC 8174; область действия нормативной формулировки, не owner, reviewer, publication, correction или author development. |
Проверяемые первичные/нормативные источники до cutoff:
- [RFC 2119 — Key words for use in RFCs to Indicate Requirement Levels](https://www.rfc-editor.org/rfc/rfc2119.html), IETF RFC 2119, March 1997, immutable RFC publication. Использован для явного различения требования и рекомендации. Не подтверждает событие, review, публикацию, коррекцию или effect P118.
- [RFC 8174 — Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words](https://www.rfc-editor.org/rfc/rfc8174.html), IETF RFC 8174, May 2017, immutable RFC publication. Использован для границы сильной формулировки. Не создаёт будущий редакционный процесс или результат.
- [PROV-DM: The PROV Data Model](https://www.w3.org/TR/2013/REC-prov-dm-20130430/), W3C Recommendation, 30 April 2013, dated immutable Recommendation. Использован как vocabulary происхождения утверждений. Не доказывает provenance будущего архива, review, feedback или outcome.
`curl -I` вернул HTTP 200 для всех трёх URL. Первые два абзаца каждой статьи называют конкретную проблему и цену ошибки. Каждая статья имеет собственные таблицу, SVG с русскими `alt`/подписью, безопасный runnable example, ordered process, ограничения и следующий шаг. Голос М10 держит прагматичную рамку «условие → риск → проверка → действие»; словарь local editorial standards и voice trajectory используется только как future contract vocabulary, не как свидетельство будущего прохода.
Текущий evidence/temporal/voice review plan-files: **pass**. Это результат проверки формы в 2026-07-31, не заявление о будущем редакционном результате.
## Проход 2 — literal safety и fail closed
`author-manifesto-plan-v1` — единственный accepted fixed literal. Factory делает JSON-clone и recursive deep freeze. Evaluator принимает literal только при exact JSON equality с закрытым named набором; неизвестный object не нормализуется и не получает fallback.
| Fixture literal | Ожидаемый статус | Фактический статус |
| --- | --- | --- |
| `author-manifesto-plan-v1` | `synthetic-plan-hand-off` | pass |
| `author-manifesto-undated-v1` | `stop-temporal-boundary-required` | pass |
| `author-manifesto-unknown-source-v1` | `stop-unknown-source-set` | pass |
| `author-manifesto-unknown-question-v1` | `stop-unknown-question` | pass |
| `author-manifesto-hidden-review-v1` | `stop-hidden-review-input` | pass |
| `author-manifesto-declared-quality-v1` | `stop-declared-quality-pass-publication-correction-or-outcome` | pass |
| `author-manifesto-disallowed-output-v1` | `stop-disallowed-positive-result` | pass |
| ad-hoc unknown object | `stop-unknown-fixed-literal` | pass |
- Exact editor date, plan date и source cutoff обязательны; unknown source/date/question fail closed.
- `reviewInputs.evidence`, `temporal` и `voice` обязаны остаться `not-provided`; hidden review input останавливает путь.
- `quality`, `pass`, `publication`, `correction` и `outcome` обязаны остаться `not-declared`; объявленный future result останавливает путь.
- Fixture: **11/11** — 8 literal statuses и 3 article checks. `productionEffect: not-attempted` сохраняется в каждом output.
## Проход 3 — visual / mobile / a11y / safety и duplicate scan
- SVG имеют русские `<title>` и `<desc>`; в каждой статье есть отдельные русские `alt` и `figcaption`. Граф решения, matrix рубрики и loop hand-off объясняют разные механизмы, а не иллюстрируют выдуманную публикационную историю.
- `xmllint --noout` прошёл для всех трёх SVG. Safety scan для `<script>`, `<foreignObject>`, `javascript:`, `data:image` и inline event-handler attributes вернул пустой output: pass.
- Sharp отрендерил 3 PNG шириной **375 px**. Ручной просмотр фактических рендеров: граф решения сохраняет входы, positive и stop path; matrix сохраняет три читаемые строки проходов; hand-off loop сохраняет разрыв перед выдуманным review. Обрезаний и наложений не обнаружено.
- `npm run audit:draft -- scripts/upgrade-2027-12.mjs` прошёл для трёх exact slug и body lengths 10 874 / 11 177 / 10 831. npm вывел существующие предупреждения об unknown user config `store-dir`, `cache-dir`, `public-hoist-pattern`; audit завершился успешно.
- Strict duplicate scan исключал section `Проверяемые источники`, но включал `<p>` и `<pre>`, поэтому runnable code участвовал. P102–P115 взяты из `upgrade-2026-08.mjs` … `upgrade-2027-09.mjs`; P116–P117 — из существующих body в `articles.json` только для read-only comparison. Сканированы **48** prior articles; выполнено **147** comparisons: 144 P118-to-prior и 3 внутренних. Exact paragraphs длиной 160+ и common 12-word fragments: `hits: []`.
## Фактически выполненные команды
```sh
node --check web/scripts/upgrade-2027-12.mjs
node web/scripts/upgrade-2027-12.mjs --verify-fixture
cd web && npm run audit:draft -- scripts/upgrade-2027-12.mjs
xmllint --noout public/assets/editorial/2027/author-manifesto-2027-editorial-decision-graph.svg public/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg public/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg
rg -n -i '<(script|foreignObject)\b|javascript:|data:image|(?:^|[[:space:]])on[a-z]+=' public/assets/editorial/2027/author-manifesto-2027-editorial-decision-graph.svg public/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg public/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg
node --input-type=module -e 'import sharp from "./web/node_modules/sharp/lib/index.js"; /* SVG → 375 px PNG */'
curl -I https://www.rfc-editor.org/rfc/rfc2119.html
curl -I https://www.rfc-editor.org/rfc/rfc8174.html
curl -I https://www.w3.org/TR/2013/REC-prov-dm-20130430/
node --input-type=module -e '/* P102–P117 source-excluded, code-included duplicate scan */'
```
## Disposition
Изменены только разрешённые P118-файлы:
- `web/scripts/upgrade-2027-12.mjs`
- `editorial/reviews/2027-12-draft.md`
- `web/public/assets/editorial/2027/author-manifesto-2027-editorial-decision-graph.svg`
- `web/public/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg`
- `web/public/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg`
Git не использовался. Registry, README, очередь, `articles.json`, app и чужие пакеты не изменялись. P118 остаётся плановым пакетом: будущая review, publication, correction или effect потребуют собственного authorised scope и не могут быть добавлены к этому сценарию задним числом.
## Независимое приёмочное ревью
- RFC 2119 и RFC 8174 повторно сверены как BCP-публикации March 1997 и May 2017; PROV-DM — как dated W3C Recommendation 30 April 2013. В финальной серии они используются только для точности модальных требований и терминологии происхождения, не как доказательство авторского развития или будущего выпуска.
- Повторно выполнены `node --check`, fixture (**11/11**), `npm run audit:draft` и все три runnable-примера. Каждый возвращает только named fixed literal с `synthetic-plan-hand-off`; никакой review, публикация, correction или outcome не создаются.
- Три SVG прошли XML/safety scan и независимый рендер Sharp на 375 px. Все диаграммы содержат читаемые заголовки, входы, hand-off и stop-ветки; обрезаний, наложений и внешнего содержимого нет.
- Независимый strict scan сначала обнаружил общий 12-словный фрагмент с P116 в первом абзаце practice-статьи. Формулировка переписана, затем скан P102–P118 повторён: **51 статья**, **1 275 парных сравнений**, sources исключены, code включён, common 12-word fragments — `[]`.
- Приёмочный verdict: **pass**. Пакет включён в registry как 356–358-й материал; production build и публикация выполняются после общей проверки интеграции.
+2
View File
@@ -114,6 +114,7 @@ import { revisions as august2027Revisions } from '../scripts/upgrade-2027-08.mjs
import { revisions as september2027Revisions } from '../scripts/upgrade-2027-09.mjs';
import { revisions as october2027Revisions } from '../scripts/upgrade-2027-10.mjs';
import { revisions as november2027Revisions } from '../scripts/upgrade-2027-11.mjs';
import { revisions as december2027Revisions } from '../scripts/upgrade-2027-12.mjs';
// This layer replaces archived source entries without losing their stable slug and date.
export const editorialRevisions = [
@@ -233,4 +234,5 @@ export const editorialRevisions = [
...september2027Revisions,
...october2027Revisions,
...november2027Revisions,
...december2027Revisions,
];
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
<title id="title">Граф решения перед техническим советом</title>
<desc id="desc">Три входа: вопрос, проверяемый источник и граница вывода. Только точные literals ведут к синтетической передаче плана. Неизвестный источник или заявленный результат ведут к остановке.</desc>
<defs>
<linearGradient id="bg" x1="0" x2="1" y1="0" y2="1"><stop stop-color="#10233F"/><stop offset="1" stop-color="#183B5C"/></linearGradient>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M0 0L12 6L0 12z" fill="#8ED1C0"/></marker>
<marker id="stop" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M0 0L12 6L0 12z" fill="#FF897A"/></marker>
</defs>
<rect width="1200" height="720" rx="36" fill="url(#bg)"/>
<text x="70" y="78" fill="#F6FAFF" font-family="Arial, sans-serif" font-size="34" font-weight="700">Совет начинается с карточки решения</text>
<text x="70" y="116" fill="#B8CCE1" font-family="Arial, sans-serif" font-size="22">P118: форма вопроса, а не история будущего выпуска</text>
<g font-family="Arial, sans-serif">
<rect x="70" y="190" width="270" height="150" rx="20" fill="#224E71" stroke="#83D3C0" stroke-width="3"/>
<text x="102" y="238" fill="#FFFFFF" font-size="26" font-weight="700">1. Вопрос</text>
<text x="102" y="276" fill="#CFE5F4" font-size="21">Какое решение</text><text x="102" y="305" fill="#CFE5F4" font-size="21">нельзя принять?</text>
<rect x="70" y="430" width="270" height="150" rx="20" fill="#224E71" stroke="#83D3C0" stroke-width="3"/>
<text x="102" y="478" fill="#FFFFFF" font-size="26" font-weight="700">2. Источник</text>
<text x="102" y="516" fill="#CFE5F4" font-size="21">Дата, URL, версия</text><text x="102" y="545" fill="#CFE5F4" font-size="21">и предел применения</text>
<rect x="430" y="310" width="300" height="160" rx="20" fill="#176B67" stroke="#A9E5D4" stroke-width="4"/>
<text x="466" y="360" fill="#FFFFFF" font-size="27" font-weight="700">Exact literal</text>
<text x="466" y="400" fill="#D7FFF4" font-size="22">граница вывода</text><text x="466" y="430" fill="#D7FFF4" font-size="22">без review и outcome</text>
<rect x="820" y="250" width="305" height="170" rx="20" fill="#1D785A" stroke="#B9EDCE" stroke-width="4"/>
<text x="851" y="304" fill="#FFFFFF" font-size="25" font-weight="700">synthetic-plan-</text><text x="851" y="337" fill="#FFFFFF" font-size="25" font-weight="700">hand-off</text>
<text x="851" y="378" fill="#D4F7E3" font-size="20">productionEffect:</text><text x="851" y="405" fill="#D4F7E3" font-size="20">not-attempted</text>
<rect x="820" y="505" width="305" height="112" rx="20" fill="#823B46" stroke="#FF9A8A" stroke-width="4"/>
<text x="852" y="552" fill="#FFFFFF" font-size="26" font-weight="700">STOP</text><text x="852" y="584" fill="#FFE1DC" font-size="20">unknown / declared</text>
</g>
<path d="M340 265H405Q425 265 425 310" fill="none" stroke="#8ED1C0" stroke-width="6" marker-end="url(#arrow)"/>
<path d="M340 505H390Q425 505 425 470" fill="none" stroke="#8ED1C0" stroke-width="6" marker-end="url(#arrow)"/>
<path d="M730 390H790" fill="none" stroke="#8ED1C0" stroke-width="7" marker-end="url(#arrow)"/>
<path d="M580 470V560Q580 590 810 590" fill="none" stroke="#FF897A" stroke-width="6" stroke-dasharray="12 12" marker-end="url(#stop)"/>
<text x="488" y="530" fill="#FFB2A7" font-family="Arial, sans-serif" font-size="19">unknown source / claim</text>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
<title id="title">Матрица рубрики качества инженерного текста</title>
<desc id="desc">Три независимых прохода: evidence, temporal и voice. Каждый требует названный вход; неизвестный вход или заявленный pass приводят к остановке, а не к оценке качества.</desc>
<rect width="1200" height="720" rx="36" fill="#F5F8FC"/>
<text x="65" y="76" fill="#142B45" font-family="Arial, sans-serif" font-size="34" font-weight="700">Три прохода не складываются в общий stamp</text>
<text x="65" y="113" fill="#526B82" font-family="Arial, sans-serif" font-size="22">Каждый проход проверяет свой риск и может остановить hand-off</text>
<g font-family="Arial, sans-serif">
<rect x="65" y="165" width="1070" height="74" rx="14" fill="#193B5D"/>
<text x="100" y="211" fill="#FFFFFF" font-size="23" font-weight="700">Проход</text><text x="355" y="211" fill="#FFFFFF" font-size="23" font-weight="700">Что различает</text><text x="705" y="211" fill="#FFFFFF" font-size="23" font-weight="700">Stop без входа</text>
<rect x="65" y="239" width="1070" height="105" fill="#E8F2FB"/>
<text x="100" y="287" fill="#183A58" font-size="27" font-weight="700">Evidence</text><text x="355" y="280" fill="#254C6C" font-size="22">claim / источник / boundary</text><text x="705" y="280" fill="#A23F48" font-size="21">not-provided</text><text x="705" y="310" fill="#A23F48" font-size="21">не становится verdict</text>
<rect x="65" y="344" width="1070" height="105" fill="#F2F6FA"/>
<text x="100" y="392" fill="#183A58" font-size="27" font-weight="700">Temporal</text><text x="355" y="385" fill="#254C6C" font-size="22">editorial date / plan / cutoff</text><text x="705" y="385" fill="#A23F48" font-size="21">unknown date</text><text x="705" y="415" fill="#A23F48" font-size="21">закрывает маршрут</text>
<rect x="65" y="449" width="1070" height="105" fill="#E8F2FB"/>
<text x="100" y="497" fill="#183A58" font-size="27" font-weight="700">Voice</text><text x="355" y="490" fill="#254C6C" font-size="22">проблема / цена / действие / limit</text><text x="705" y="490" fill="#A23F48" font-size="21">claimed pass</text><text x="705" y="520" fill="#A23F48" font-size="21">не подменяет review</text>
<rect x="65" y="594" width="1070" height="70" rx="18" fill="#1E765D"/>
<text x="101" y="638" fill="#FFFFFF" font-size="26" font-weight="700">Только все named fixed states → synthetic-plan-hand-off</text>
</g>
<path d="M320 239V554M670 239V554" stroke="#B4C9DC" stroke-width="3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
<title id="title">Петля корректирующей передачи без выдуманного review</title>
<desc id="desc">Вопрос и временная граница проходят только при отсутствии review inputs и деклараций качества. Любое заявленное review, публикация, correction или outcome останавливает передачу.</desc>
<defs><marker id="greenArrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M0 0L12 6L0 12z" fill="#56C29B"/></marker><marker id="redArrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M0 0L12 6L0 12z" fill="#E96F69"/></marker></defs>
<rect width="1200" height="720" rx="36" fill="#101F35"/>
<text x="65" y="78" fill="#F4F9FF" font-family="Arial, sans-serif" font-size="34" font-weight="700">Корректирующий hand-off не выдумывает петлю revision</text>
<text x="65" y="115" fill="#AFC4DA" font-family="Arial, sans-serif" font-size="22">P118 передаёт границу вопроса и останавливается перед feedback</text>
<g font-family="Arial, sans-serif">
<circle cx="210" cy="382" r="112" fill="#204968" stroke="#77C6B3" stroke-width="4"/>
<text x="152" y="367" fill="#FFFFFF" font-size="27" font-weight="700">Вопрос</text><text x="128" y="402" fill="#D2E6F3" font-size="20">claim остаётся</text><text x="148" y="430" fill="#D2E6F3" font-size="20">вопросом</text>
<circle cx="505" cy="382" r="112" fill="#204968" stroke="#77C6B3" stroke-width="4"/>
<text x="436" y="367" fill="#FFFFFF" font-size="26" font-weight="700">Граница</text><text x="422" y="402" fill="#D2E6F3" font-size="20">даты и cutoff</text><text x="424" y="430" fill="#D2E6F3" font-size="20">exact literal</text>
<circle cx="800" cy="382" r="112" fill="#146A61" stroke="#9DE0C8" stroke-width="4"/>
<text x="740" y="367" fill="#FFFFFF" font-size="25" font-weight="700">Hand-off</text><text x="730" y="402" fill="#D9FFF1" font-size="20">not-declared</text><text x="716" y="430" fill="#D9FFF1" font-size="20">not-attempted</text>
<rect x="910" y="526" width="220" height="100" rx="18" fill="#7E3844" stroke="#FF9087" stroke-width="4"/>
<text x="945" y="568" fill="#FFFFFF" font-size="25" font-weight="700">STOP</text><text x="937" y="600" fill="#FFE3DE" font-size="18">claimed result</text>
</g>
<path d="M322 382H380" fill="none" stroke="#56C29B" stroke-width="7" marker-end="url(#greenArrow)"/>
<path d="M617 382H675" fill="none" stroke="#56C29B" stroke-width="7" marker-end="url(#greenArrow)"/>
<path d="M800 270C800 180 505 175 505 250" fill="none" stroke="#56C29B" stroke-width="5" stroke-dasharray="11 11" marker-end="url(#greenArrow)"/>
<text x="526" y="186" fill="#9DE0C8" font-family="Arial, sans-serif" font-size="19">новый scope решает, нужен ли review</text>
<path d="M850 460C900 490 935 510 955 525" fill="none" stroke="#E96F69" stroke-width="7" marker-end="url(#redArrow)"/>
<text x="814" y="502" fill="#FFAAA2" font-family="Arial, sans-serif" font-size="18">review / publication / correction / outcome</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+202
View File
@@ -0,0 +1,202 @@
const escapeHtml = (value) => String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&#039;');
const p = (text) => `<p>${text}</p>`;
const h2 = (text) => `<h2>${text}</h2>`;
const code = (text) => `<pre><code>${escapeHtml(text)}</code></pre>`;
const ol = (items) => `<ol>${items.map((item) => `<li>${item}</li>`).join('')}</ol>`;
const figure = (src, alt, caption) => `<figure><img src="${src}" alt="${alt}" loading="lazy" /><figcaption>${caption}</figcaption></figure>`;
const table = (caption, headers, rows) => `<div class="table-scroll"><table><caption>${caption}</caption><thead><tr>${headers.map((cell) => `<th scope="col">${cell}</th>`).join('')}</tr></thead><tbody>${rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`;
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>Проверяемые источники<\/h2>[\s\S]*$/, '')); }
const REFERENCES = deepFreeze({
rfc2119: { title: 'RFC 2119 — Key words for use in RFCs to Indicate Requirement Levels', url: 'https://www.rfc-editor.org/rfc/rfc2119.html', version: 'IETF RFC 2119, March 1997, immutable RFC publication' },
rfc8174: { title: 'RFC 8174 — Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words', url: 'https://www.rfc-editor.org/rfc/rfc8174.html', version: 'IETF RFC 8174, May 2017, immutable RFC publication' },
provdm: { title: 'PROV-DM: The PROV Data Model', url: 'https://www.w3.org/TR/2013/REC-prov-dm-20130430/', version: 'W3C Recommendation, 30 April 2013, dated immutable Recommendation' },
});
function sources(entries) { return `<ul>${entries.map(({ key, use, boundary }) => { const ref = REFERENCES[key]; return `<li><a href="${ref.url}" target="_blank" rel="noopener noreferrer">${escapeHtml(ref.title)}</a> — ${escapeHtml(ref.version)}. Применение: ${escapeHtml(use)} Граница: ${escapeHtml(boundary)}</li>`; }).join('')}</ul>`; }
const FIXED_MANIFESTO_LITERALS = deepFreeze({
'author-manifesto-plan-v1': { id: 'author-manifesto-plan-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-undated-v1': { id: 'author-manifesto-undated-v1', editorialDate: '', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-unknown-source-v1': { id: 'author-manifesto-unknown-source-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'unknown-source-set', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-unknown-question-v1': { id: 'author-manifesto-unknown-question-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'unknown-question', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-hidden-review-v1': { id: 'author-manifesto-hidden-review-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'claimed', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-declared-quality-v1': { id: 'author-manifesto-declared-quality-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'passed', pass: 'claimed', publication: 'published', correction: 'issued', outcome: 'successful' }, requestedOutput: 'synthetic-plan-hand-off' },
'author-manifesto-disallowed-output-v1': { id: 'author-manifesto-disallowed-output-v1', editorialDate: '2026-07-31', planDate: '2027-12', sourceCutoff: '2026-07-31', sourceSet: 'author-manifesto-official-sources-v1', question: 'author-manifesto-decision-record-before-advice-v1', reviewInputs: { evidence: 'not-provided', temporal: 'not-provided', voice: 'not-provided' }, declarations: { quality: 'not-declared', pass: 'not-declared', publication: 'not-declared', correction: 'not-declared', outcome: 'not-declared' }, requestedOutput: 'published-manifesto' },
});
export function createAuthorManifestoLiteral(id = 'author-manifesto-plan-v1') { const literal = FIXED_MANIFESTO_LITERALS[id]; return literal ? deepFreeze(cloneFixed(literal)) : undefined; }
function stop(status, reason) { return deepFreeze({ status, reason, productionEffect: 'not-attempted', nextAction: 'open-a-separate-authorized-scope-only-when-a-decision-needs-evidence' }); }
export function assessAuthorManifestoPlan(input) {
const known = Object.values(FIXED_MANIFESTO_LITERALS).some((literal) => JSON.stringify(literal) === JSON.stringify(input));
if (!known) return stop('stop-unknown-fixed-literal', 'input-is-not-a-known-named-fixed-literal');
if (input.editorialDate !== '2026-07-31' || input.planDate !== '2027-12' || input.sourceCutoff !== '2026-07-31') return stop('stop-temporal-boundary-required', 'editorial-date-plan-date-and-source-cutoff-must-be-exact');
if (input.sourceSet !== 'author-manifesto-official-sources-v1') return stop('stop-unknown-source-set', 'source-set-must-be-a-known-fixed-literal');
if (input.question !== 'author-manifesto-decision-record-before-advice-v1') return stop('stop-unknown-question', 'question-must-be-a-known-fixed-literal');
if (Object.values(input.reviewInputs).some((value) => value !== 'not-provided')) return stop('stop-hidden-review-input', 'evidence-temporal-and-voice-inputs-cannot-be-claimed-by-a-plan');
if (Object.values(input.declarations).some((value) => value !== 'not-declared')) return stop('stop-declared-quality-pass-publication-correction-or-outcome', 'a-plan-cannot-declare-quality-pass-publication-correction-or-outcome');
if (input.requestedOutput !== 'synthetic-plan-hand-off') return stop('stop-disallowed-positive-result', 'the-only-accepted-positive-output-is-synthetic-plan-hand-off');
return deepFreeze({ status: 'synthetic-plan-hand-off', literalId: input.id, planDate: input.planDate, question: input.question, reviewInputs: deepFreeze(cloneFixed(input.reviewInputs)), declarations: deepFreeze(cloneFixed(input.declarations)), productionEffect: 'not-attempted', nextAction: 'preserve-the-question-without-claiming-a-future-editorial-result' });
}
export function inspectDecisionRecordLiteral() { const output = assessAuthorManifestoPlan(createAuthorManifestoLiteral()); return deepFreeze({ question: output.question, status: output.status, productionEffect: output.productionEffect }); }
export function inspectRubricLiteral() { const output = assessAuthorManifestoPlan(createAuthorManifestoLiteral()); return deepFreeze({ evidence: output.reviewInputs.evidence, temporal: output.reviewInputs.temporal, voice: output.reviewInputs.voice, status: output.status }); }
export function inspectHandoffLiteral() { const output = assessAuthorManifestoPlan(createAuthorManifestoLiteral()); return deepFreeze({ quality: output.declarations.quality, correction: output.declarations.correction, outcome: output.declarations.outcome, status: output.status }); }
export function runAuthorManifestoFixture() {
const expected = [['author-manifesto-plan-v1', 'synthetic-plan-hand-off'], ['author-manifesto-undated-v1', 'stop-temporal-boundary-required'], ['author-manifesto-unknown-source-v1', 'stop-unknown-source-set'], ['author-manifesto-unknown-question-v1', 'stop-unknown-question'], ['author-manifesto-hidden-review-v1', 'stop-hidden-review-input'], ['author-manifesto-declared-quality-v1', 'stop-declared-quality-pass-publication-correction-or-outcome'], ['author-manifesto-disallowed-output-v1', 'stop-disallowed-positive-result']];
const checks = expected.map(([id, status]) => ({ id, expected: status, actual: assessAuthorManifestoPlan(createAuthorManifestoLiteral(id)).status }));
const unknown = assessAuthorManifestoPlan({ id: 'not-fixed' });
return deepFreeze({ passed: checks.filter((item) => item.expected === item.actual).length + Number(unknown.status === 'stop-unknown-fixed-literal'), total: checks.length + 1, accepted: checks.every((item) => item.expected === item.actual) && unknown.status === 'stop-unknown-fixed-literal', checks: deepFreeze(checks) });
}
function revision(meta, parts, referenceEntries) {
const contentHtml = parts.join('') + h2('Проверяемые источники') + sources(referenceEntries);
const proseLength = bodyText(contentHtml).length;
if (proseLength < 10000 || proseLength > 13000) throw new Error(`${meta.slug}: body length ${proseLength}`);
return deepFreeze({ ...meta, contentHtml, proseLength });
}
const practiceRefs = [
{ key: 'rfc2119', use: 'Даёт датированный нормативный словарь для явного различения требования и рекомендации в карточке решения.', boundary: 'Не подтверждает существование статьи, решения, редактора, review или публикации P118.' },
{ key: 'provdm', use: 'Даёт терминологию сущности, действия и происхождения для объяснения, почему совету нужен проверяемый след.', boundary: 'Не доказывает происхождение локального архива, исправления, отзыв читателя или эффект.' },
];
const mechanismRefs = [
{ key: 'rfc8174', use: 'Уточняет чтение нормативных слов и помогает отделить проверяемую границу от риторического усиления.', boundary: 'Не задаёт редакционный процесс, уровень качества, результат прохода или факт выпуска.' },
{ key: 'provdm', use: 'Поддерживает различение утверждения и его происхождения при описании уровней evidence.', boundary: 'Не является доказательством реальных входов review, оценок, коррекций или результатов P118.' },
];
const fieldRefs = [
{ key: 'rfc2119', use: 'Фиксирует точность модальных форм, когда hand-off запрещает подменять неизвестное обязательством.', boundary: 'Не назначает владельца, получателя, публикацию, исправление или author development.' },
{ key: 'rfc8174', use: 'Помогает показать, что сильная формулировка должна иметь определённую область действия.', boundary: 'Не подтверждает прохождение review, feedback, корректирующей работы или production effect.' },
];
const practice = revision({ slug: 'editorial-2027-12-practice-author-manifesto', title: 'Инженерное письмо: сначала карточка решения, потом технический совет', categories: ['Практика', 'Техническое письмо'], cover: '/assets/editorial/2027/author-manifesto-2027-editorial-decision-graph.svg', excerpt: 'План на декабрь 2027: как не превратить полезный совет в правдоподобную, но не проверяемую историю.', readingMinutes: 28 }, [
p('P118 — сценарий на декабрь 2027: редакционная дата 2026-07-31, а граница источников зафиксирована тем же срезом. Точная проблема инженерного письма возникает до первого абзаца: автор хочет дать совет, но не записывает, какое решение этот совет должен поддержать. Тогда текст смешивает условие, предположение и действие. Цена ошибки практическая: следующий инженер принимает гладкую формулировку за готовый маршрут и применяет её там, где граница системы, версия или риск совсем другие.'),
p('Вторая стоимость появляется, когда карточку решения заменяют «опытом автора». В будущем выпуске легко написать, что совет уже помог, что его проверяли или что он вырос из набора прошлых материалов. Для P118 это запрещённый narrative: нет будущей статьи, публикации, редактора, разбора, обратной связи, коррекции, команды, метрики и production effect. Единственный положительный результат evaluator — <code>synthetic-plan-hand-off</code>; вместе с ним всегда остаётся <code>productionEffect: not-attempted</code>.'),
h2('Совет начинается с решения, которого ещё нет'),
p('Под «карточкой решения» здесь понимается не ADR и не заявка на изменение. Это короткая форма, которая заставляет назвать вопрос, допустимый источник, границу вывода и следующий шаг. В P118 она существует только как named fixed in-memory literal <code>author-manifesto-decision-record-before-advice-v1</code>. Его нельзя принять за файл, запись, шаблон команды или будущий процесс. Literal нужен, чтобы проверить форму рассуждения, а не сообщить о том, что решение когда-либо было принято.'),
p('Технический совет без вопроса опасен не потому, что он обязательно неверен. Он опасен тем, что не говорит, что именно опровергнет его применимость. Фраза «добавьте проверку» не указывает вход, требуемое свойство, владельца исключения и цену отказа. В карточке вместо этого сначала появляется один вопрос: какое решение нельзя принять, пока неизвестны границы claim. До ответа на него команда не выбирает библиотеку, не пишет правило и не обещает выпуск.'),
p('Такой порядок особенно полезен в долгих текстах. Чем убедительнее объяснение, тем проще читателю забыть, откуда взялась исходная посылка. Короткое поле source boundary возвращает разговор к проверяемому: нормативный документ может объяснить значение слова, но не может доказать факт в чужом проекте. Прошлая заметка могла бы быть историческим материалом, но в этом плановом scope она не является evidence. Поэтому P118 не строит цепочку «архив → правило → успех».'),
table('Карточка перед техническим советом', ['Поле', 'Значение в P118', 'Что разрешает', 'Чего не разрешает'], [
['Вопрос', '<code>author-manifesto-decision-record-before-advice-v1</code>', 'Сохранить предмет будущего решения', 'Принять решение или назвать его владельца'],
['Источник', '<code>author-manifesto-official-sources-v1</code>', 'Проверить терминологическую границу', 'Считать официальный текст свидетельством локального события'],
['Вход review', '<code>not-provided</code>', 'Признать отсутствие входа', 'Сказать, что текст был прочитан или оценён'],
['Декларация', '<code>not-declared</code>', 'Не смешивать форму и результат', 'Объявить качество, pass, публикацию или коррекцию'],
['Выход', '<code>synthetic-plan-hand-off</code>', 'Передать вопрос дальше', 'Обещать production effect'],
]),
h2('Граница между рецептом и утверждением'),
p('Рецепт отвечает на вопрос «что попробовать при известном симптоме». Утверждение отвечает на другой вопрос: «почему это действие относится к данной причине». В инженерной статье оба слоя нужны, но их нельзя маскировать один другим. Если указан только рецепт, reader не понимает, когда остановиться. Если указан только вывод, reader не может повторить проверку. Карточка решения заставляет держать слой действия рядом с его ограничением.'),
p('Например, совет о логировании не должен начинаться с названия поля. Сначала автор определяет решение: достаточно ли данных, чтобы различить два состояния. Затем называет допустимые входы, чувствительные поля и условие, при котором лог ничего не доказывает. Только после этого возможен пример формата события. P118 не содержит такой реальный event и не предлагает его собрать. Он показывает последовательность вопросов, которую будущий scope обязан сделать явной.'),
p('У этой дисциплины есть стоимость: начало статьи становится медленнее, а заголовок не обещает универсальную кнопку. Зато читатель видит, где заканчивается утверждение. Это снижает blast radius советов, которые кажутся мелкими: параметр timeout, признак готовности, правило миграции или формат review могут менять поведение целого контура. Писать «лучше всегда» дешевле, чем назвать границу, но именно эта экономия создаёт дорогую диагностику позже.'),
figure('/assets/editorial/2027/author-manifesto-2027-editorial-decision-graph.svg', 'Граф решения: вопрос, источник и граница вывода сходятся в синтетический hand-off; неизвестный источник или заявленный результат ведут к остановке.', 'Схема показывает форму планового решения для P118. Она не сообщает о реальной статье, архиве, редакционном действии, публикации или исправлении.'),
h2('Runnable пример оставляет совет неполным'),
p('Пример ниже безопасно запускается в Node рядом с модулем. Он не читает файлы, сеть, environment, clock, secrets, telemetry, system или data. Он берёт только зашитый literal и возвращает статус его формы. Поэтому вывод не является оценкой текста и не указывает, какой совет нужно написать. Не добавляйте сюда repository path, issue, автора или реалистичный контекст: такие детали выглядели бы как скрытые входы, которых у P118 нет.'),
code("import { inspectDecisionRecordLiteral } from './upgrade-2027-12.mjs';\n\nconst card = inspectDecisionRecordLiteral();\nconsole.log(card.question, card.status, card.productionEffect);\n// author-manifesto-decision-record-before-advice-v1 synthetic-plan-hand-off not-attempted"),
h2('Порядок работы до появления текста'),
ol(['Назвать одно решение, которое без дополнительного evidence нельзя принять, и оставить его вопросом, а не выводом.', 'Зафиксировать editorial date, planDate и source cutoff; будущий период не даёт права описывать будущие события.', 'Выбрать только проверяемые источники и рядом написать, что каждый из них не может подтвердить.', 'Отделить рецептурный шаг от factual claim: для каждого указать условия применимости и условие остановки.', 'Если требуется review, publication, correction или feedback, перенести их в другой authorised scope с собственными входами.', 'Передать исходный вопрос как <code>synthetic-plan-hand-off</code>, не заменяя его декларацией качества.']),
h2('Почему декларация не заменяет решение'),
p('Слово «манифест» часто провоцирует обратный порядок: сначала объявить принцип, затем подобрать примеры. Для технического письма это слабая конструкция. Принцип без decision record не показывает, кто должен действовать, какой вход допустим и чем можно опровергнуть правило. P118 использует название выпуска как тему проверки: не лозунг о письме, а разбор того, какую форму должен иметь совет до появления уверенного тона.'),
p('Даже аккуратная формула «пишите доказуемо» недостаточна. Неясно, о доказательстве какого типа идёт речь: ссылка на норму, воспроизводимый запуск, наблюдение в среде или вывод из нескольких артефактов. Эти типы нельзя свести к одному слову evidence. Карточка решения сначала фиксирует вид вопроса, а следующий scope выбирает метод. Так future reader не получит ложного разрешения заменить отсутствующий trace ссылкой на стандарт.'),
p('Уместный технический язык здесь прямой. Вместо «повысим качество текста» лучше спросить: какое утверждение нельзя проверить без источника, даты или допустимого ввода? Вместо «учтём контекст» — какой контекст меняет выбор действия? Вместо «сформируем культуру» — кому принадлежит решение открыть новый scope? Такие замены не делают прозу холоднее; они возвращают ей объект, действие и проверку.'),
p('План не обязан заранее решить, будет ли следующий scope создан. Может оказаться, что вопрос снимается без исследования, потому что изменение не требуется. Может оказаться, что нужный вход нельзя законно или безопасно собрать. Может оказаться, что совет нужно сузить до условия версии. Любой из этих результатов совместим с хорошей карточкой. Несовместимо только одно: описать один из вариантов как уже случившийся в будущем декабре 2027.'),
h2('Как карточка удерживает цену решения'),
p('Цена решения не сводится к числу часов. У совета есть цена неверного применения, цена объяснения исключения, цена обратимости и цена поддержки следующего читателя. Если автор не называет хотя бы одну из них, рекомендация обычно выглядит дешевле, чем есть. Карточка не обязана рассчитывать стоимость. Ей достаточно обозначить, какое последствие меняет выбор: риск потерять данные, расширить доступ, скрыть деградацию или закрепить неподдерживаемую зависимость.'),
p('Такой подход меняет работу с примером. Код в статье не служит знаком компетентности и не должен быть самым убедительным элементом страницы. Он отвечает на узкий вопрос: как выглядит действие после того, как условия названы. Рядом полезно сказать, чего пример не содержит: production configuration, реальный request, секрет, профиль нагрузки и подтверждённый результат. Тогда читатель не переносит литературную компактность кода на свою систему без проверки.'),
p('Решение также нуждается в обратимости. Если совет можно отменить только ценой миграции, потери совместимости или ручного восстановления, это не повод отказаться от него, но повод записать границу заранее. В P118 нет конкретного изменения, поэтому нет и обещания rollback. Будущий scope должен отдельно определить, что будет означать отказ от выбранного пути. Нельзя подменять эту работу фразой «при необходимости откатим»: она скрывает owner, данные и порядок восстановления.'),
p('Полезная карточка различает адресата текста и владельца решения. Статья может быть написана для инженера, но не назначает ему право менять систему. Совпадение этих ролей нельзя предполагать по должности или тону. В данном сценарии оба поля сознательно отсутствуют: P118 не знает будущей команды. Благодаря этому advice остаётся техническим артефактом, а не неявным приказом тому, кто первым его прочитает.'),
p('Наконец, advice должен переживать неизвестность. Если новая версия, другой источник или более узкий риск меняют вывод, текст не считается провалом. Провалом будет скрыть условие и заставить reader обнаружить его в production. Поэтому практическая форма манифеста — не набор правил, а привычка оставлять у утверждения место для опровержения. Она делает следующий разговор короче, потому что участники спорят о названном условии, а не о стиле автора.'),
h2('Ограничения и следующий шаг'),
p('P118 practice не является editorial policy, ADR, историей публикаций, каталогом статей, review record, evidence package или рекомендацией для конкретной команды. Official sources здесь задают язык границ, но не являются доказательством местного решения. Fixed literals не принимают пользовательский input и не моделируют проект. Fixture проверяет закрытый набор значений; он не проверяет ясность будущей статьи и не измеряет её эффект.'),
p('Следующий шаг возможен только в отдельном authorised scope, если конкретный технический вопрос потребует решения. В нём надо назвать допустимые inputs, владельца решения, метод проверки, правила redaction и возможность отрицательного вывода. До этого P118 сохраняет честно ограниченную форму: вопрос остаётся вопросом, а результат — только <code>synthetic-plan-hand-off</code> с <code>productionEffect: not-attempted</code>.')
], practiceRefs);
const mechanism = revision({ slug: 'editorial-2027-12-mechanism-author-manifesto', title: 'Инженерное письмо: рубрика качества вместо заявленного качества', categories: ['Процесс', 'Техническое письмо'], cover: '/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg', excerpt: 'План на декабрь 2027: как проверять claims и границы тремя проходами, не объявляя pass и не выдумывая evidence.', readingMinutes: 29 }, [
p('P118 — плановый сценарий на 2027-12, составленный 2026-07-31. Механическая проблема качества текста точна: автор ставит ярлык «проверено», хотя не разделил claim, вход для проверки и условие опровержения. Цена ошибки — спор о впечатлении вместо работы с границей. В результате ссылка, пример и уверенная интонация склеиваются в один сигнал, а reader не может понять, какая часть текста относится к факту, а какая к предложенному действию.'),
p('Следующая цена — превратить три прохода review в рассказ о трёх прошедших review. У P118 нет будущих reviewer, их входов, оценок, замечаний, pass, публикации, corrections, feedback или outcome. Неизвестные данные не подставляются из local standards, trajectory или существующих неопубликованных artefacts: это vocabulary будущего контракта, а не доказательство будущего события. Единственный accepted positive output — <code>synthetic-plan-hand-off</code> с <code>productionEffect: not-attempted</code>.'),
h2('Рубрика измеряет полноту вопроса, не качество автора'),
p('Рубрика полезна, когда она не выдаёт балл за стиль. В этой статье она раскладывает фразу на четыре поля: claim, evidence level, falsifier и boundary. Claim — то, что текст предлагает считать верным. Evidence level — происхождение, которое могло бы поддержать claim. Falsifier — наблюдение или условие, после которого claim нельзя оставлять прежним. Boundary — то, чего claim не охватывает. Без этой четвёрки review быстро становится спором о том, «достаточно ли убедительно» написано.'),
p('P118 не заполняет эти поля реальным материалом. В named literal review inputs имеют значение <code>not-provided</code>. Это не отрицательная оценка и не скрытый result: input не был передан модулю. Такое различие важно для evidence levels. «Нет данных» не равно «данные слабые», а «источник известен» не равно «источник поддерживает именно этот вывод». Любая будущая рубрика должна сохранять это различие, иначе уровень доказательности станет декоративной шкалой.'),
p('Falsifiability в инженерном письме не требует научного эксперимента для каждого предложения. Она требует назвать, что изменило бы решение. Если статья рекомендует ограничить retry, опровергнуть применимость может семантика операции. Если предлагает кэш, опровергнуть её может отсутствие допустимой инвалидации. Если описывает правило review, опровергнуть его может неизвестный тип входа. В P118 эти примеры не говорят о существующей системе; они различают форму условия и утверждение о факте.'),
table('Матрица уровней evidence и фальсифицируемости', ['Уровень', 'Допустимая форма', 'Чего недостаточно', 'Вопрос falsifier'], [
['E0 — отсутствует', '<code>not-provided</code>', 'Уверенная формулировка или правдоподобный пример', 'Какой вход вообще разрешён?'],
['E1 — нормативный', 'Датированный стандарт или спецификация', 'Вывод о локальном поведении', 'Какая реализация или версия меняет чтение нормы?'],
['E2 — воспроизводимый', 'Описанный метод и сохранённый результат в отдельном scope', 'Один пересказ запуска', 'Какая одинаковая проверка даст другой результат?'],
['E3 — контекстный', 'Связанные inputs, метод и ограниченный inference', 'Метрика без условий измерения', 'Какая граница данных ломает перенос вывода?'],
['P118', 'Только форма будущей рубрики', 'Quality/pass/publication/correction/outcome', 'Почему вообще нельзя объявить результат?'],
]),
h2('Три прохода идут по разным рискам'),
p('Первый проход — evidence. Он не спрашивает, нравится ли источник. Он сопоставляет каждое существенное утверждение с происхождением: норма, версия, метод, наблюдение или неизвестность. Если claim живёт за пределами источника, рядом должна быть boundary. Для планового выпуска этот проход закономерно получает <code>not-provided</code>. Он не может превратить ссылку на RFC или W3C Recommendation в свидетельство будущего редакционного действия.'),
p('Второй проход — temporal. Он проверяет, не перескочил ли текст через дату. Здесь dates — не метаданные после публикации, а часть значения. Editorial date 2026-07-31, planDate 2027-12 и source cutoff 2026-07-31 закреплены exact literals. Любая другая дата останавливает evaluator. Тем самым формулировка «будет исправлено» остаётся недопустимой: она незаметно сообщает о том, чего в плановом scope не происходило.'),
p('Третий проход — voice. Его задача не угадать личность автора по оборотам. Он проверяет техническую плотность: первые абзацы называют проблему и цену, пример ограничен входом, таблица различает варианты, а финал оставляет следующее действие и ограничение. Local editorial standards и voice trajectory могут быть названы только как vocabulary будущего контракта. Они не доказывают, что голос уже проверялся, что editor видел статью или что какая-либо траектория продолжилась в 2027.'),
p('Три прохода нельзя склеивать в общий stamp. Хорошая ссылка не исправляет future tense. Спокойный голос не заменяет falsifier. Удобная таблица не делает недоступный input известным. Когда критерии смешаны, найденная слабость растворяется в общем «выглядит неплохо». Когда они разделены, каждый stop имеет понятную причину и не требует выдумывать среднее качество текста.'),
figure('/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg', 'Матрица рубрики: evidence, temporal и voice проходят отдельно; неизвестный вход и заявленный pass останавливают синтетический evaluator.', 'Рисунок объясняет три независимые проверки формы P118. Он не фиксирует реальный review, качество статьи, редакторский verdict или выпуск.'),
h2('Runnable пример показывает отсутствие review'),
p('Этот вызов безопасен: функции работают с immutable literals, не принимают ввод, не открывают filesystem и не обращаются к сети. Console output не означает, что evidence, temporal или voice действительно были проверены. Он лишь показывает, что valid plan хранит их как <code>not-provided</code>. Это точнее, чем подставить «pass pending»: pending уже создаёт историю процесса, которой здесь нет.'),
code("import { inspectRubricLiteral } from './upgrade-2027-12.mjs';\n\nconst rubric = inspectRubricLiteral();\nconsole.log(rubric.evidence, rubric.temporal, rubric.voice, rubric.status);\n// not-provided not-provided not-provided synthetic-plan-hand-off"),
h2('Порядок трёх проходов'),
ol(['Выписать отдельные claims и отметить, что является нормативным словом, предположением или действием.', 'Для каждого claim выбрать только известный evidence level; если вход отсутствует, оставить <code>not-provided</code>.', 'Сформулировать falsifier как условие смены решения, а не как обещание будущего теста.', 'Проверить exact temporal boundary: редакторская дата, planDate и cutoff не могут быть заполнены по умолчанию.', 'Прочитать текст в голосовом проходе: убрать лозунг, вернуть предмет, цену, ограничение и следующий шаг.', 'Остановить пакет, если появляется hidden review input либо declared quality, pass, publication, correction или outcome.']),
h2('Почему уровни не являются рейтингом'),
p('E3 не «лучше» E1 во всех задачах. Нормативный источник может быть достаточен, когда вопрос касается значения обязательного слова. Воспроизводимый метод нужен, когда речь о поведении конкретной версии. Контекстный inference требует ещё больше условий, но не даёт права сделать его универсальным. Рубрика работает, когда показывает соответствие формы вопроса и источника, а не когда стимулирует собирать наиболее эффектные доказательства.'),
p('Это также защищает от ложной точности. Число баллов, процент готовности или зелёный badge кажутся удобными, потому что упрощают передачу. Но при отсутствии входов они превращают unknown в красивый verdict. P118 намеренно не содержит шкалу pass/fail для будущей статьи. Его evaluator принимает один fixed literal и возвращает один hand-off; он не умеет повышать quality, выдавать publication status или считать correction завершённой.'),
p('Механизм fail closed важнее позитивной ветки. Unknown object не нормализуется до plan. Unknown source set не превращается в «источник позже уточним». Unknown question не получает стандартный текст. Claimed review не становится warning. Любое из этих состояний получает свой stop. Такой дизайн неудобен для демонстрации гладкого процесса, зато не даёт случайно использовать модуль как генератор правдоподобного editorial history.'),
p('Техническому лидеру полезно показывать эту неудобную часть. В реальной работе самый дорогой спор часто начинается не с ошибки в коде, а с невидимого перехода от наблюдения к выводу. Рубрика делает переходы явными: источник описывает норму, метод описывает воспроизводимость, reviewer input описывает review, а output описывает только форму hand-off. Если одного звена нет, цепочка не должна притворяться полной.'),
h2('Как читать claim без лишней уверенности'),
p('Claim полезно писать в проверяемом времени и области. «Этот механизм сокращает ошибки» слишком широк: неизвестны механизм, ошибка, период и способ сравнения. «Для этого входа обработчик отвергает неизвестный literal» уже связывает действие с наблюдаемым контрактом. Второе предложение не делает систему лучше само по себе, но даёт reviewer предмет для проверки. Если поле меняется, claim можно сузить или снять без спора о намерениях автора.'),
p('Evidence level не следует прятать в сноске. Когда ключевой вывод поддержан только нормативным текстом, это надо назвать возле вывода. Норма сообщает, как читать требование; она не сообщает, что конкретная реализация следует ему. Когда вывод получен запуском, читателю нужны версия, вход, метод и ограничение результата. Когда этих полей нет, аккуратный текст говорит об отсутствии основания, а не повышает убедительность прилагательными.'),
p('Falsifier должен быть доступен тому, кто принимает решение. Формула «проверим все случаи» не годится: она не определяет случаи и может оказаться невыполнимой. Гораздо полезнее назвать один класс входов, который меняет выбор, и признать остальные неизвестными. Это не упрощение ради отчёта. Такая граница определяет, сколько работы действительно нужно до решения и когда следует открыть отдельное исследование вместо бесконечной редактуры.'),
p('Temporal проход ловит не только будущие даты. Он также ловит ретроспективную причинность: «после этой статьи команда стала делать X» требует событий и связи между ними. В плановом документе даже нейтральное «позже исправим» может выглядеть как согласованный backlog. P118 избегает такого намёка. Он описывает лишь то, что другой scope мог бы решить после получения разрешённых inputs, и оставляет возможность не предпринимать ничего.'),
p('Voice проход имеет техническую цену. Абстрактная фраза заставляет reader достраивать объект; чрезмерный жаргон переносит стоимость расшифровки на менее опытного коллегу; декларация качества снимает вопрос слишком рано. Поэтому хороший поздний голос не становится авторитетнее за счёт громкости. Он становится полезнее, когда прямо называет вход, риск, решение, ограничение и следующую проверку. Рубрика удерживает эту последовательность даже там, где нет права утверждать результат.'),
h2('Ограничения и следующий шаг'),
p('P118 mechanism не является quality policy, rubric rollout, редакторским протоколом, published checklist, evidence audit или доказательством качества будущего текста. W3C и IETF sources применены к терминологии и модальным границам, не к локальной истории. The fixture не проверяет логику реального review и не делает conclusion об авторе, редакторе, архиве или продукте.'),
p('Следующий шаг требует отдельного authorised scope с разрешёнными источниками, датой, owner, способом хранения evidence и правом на отрицательный verdict. Там можно будет определить, какие levels подходят конкретному technical claim. Нельзя перенести в него объявленный результат P118: текущий сценарий заканчивается только <code>synthetic-plan-hand-off</code>, а <code>productionEffect: not-attempted</code> сохраняется на каждой ветке.')
], mechanismRefs);
const field = revision({ slug: 'editorial-2027-12-field-author-manifesto', title: 'Инженерное письмо: корректирующий hand-off без вымышленной редакционной истории', categories: ['Практика', 'Техническое письмо'], cover: '/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg', excerpt: 'План на декабрь 2027: как передать вопрос о правке и развитии автора, не подделывая feedback, публикацию или эффект.', readingMinutes: 28 }, [
p('P118 — план/scenario для 2027-12 с editorial date 2026-07-31 и source cutoff 2026-07-31. Полевая проблема корректирующей передачи начинается, когда незавершённый вопрос оформляют как историю: «получили замечание, поправили, текст стал сильнее». Цена ошибки высока: следующему человеку достаётся не вопрос, а обязательство согласиться с несуществующим feedback и продолжать якобы начатую работу. Так редакционный язык создаёт ложное происхождение решения.'),
p('Вторая стоимость — считать развитие автора доказанным по самому факту планирования. У P118 нет автора как объекта оценки, reviewer, комментария, текста будущей статьи, публикации, correction, реакции читателя, команды, метрики или production effect. Также нельзя брать local standards, trajectory, unpublished artifacts или очереди как factual evidence. Они допустимы лишь как имена будущих contract vocabulary. Accepted positive output один: <code>synthetic-plan-hand-off</code>; production effect во всех исходах — <code>not-attempted</code>.'),
h2('Коррекция начинается с неизвестного, а не с замечания'),
p('Corrective hand-off нужен не для того, чтобы послать человеку список улучшений. В строгом scope он сохраняет вопрос: какой claim потребует пересмотра, если authorised evidence появится позже. Пока evidence нет, нельзя говорить, что claim ошибочен, что редактура обнаружила проблему или что автор изменил подход. В fixed literal это отражено пятью отдельными полями: quality, pass, publication, correction и outcome имеют значение <code>not-declared</code>.'),
p('Отрицательное значение имеет происхождение. <code>not-declared</code> не означает «декларация ждёт подтверждения» и не означает «кто-то не заполнил форму». Оно означает, что P118 не создавал такого утверждения. Аналогично <code>not-provided</code> для review inputs не означает потерянный комментарий. Вход не передавался. Такая точность убирает привычное давление: будто следующий scope обязан найти подтверждение, чтобы завершить заранее написанную историю.'),
p('Author development требует ещё большей осторожности. Полезно обсуждать практику письма, но небезопасно выводить из текста личную динамику. Формулировки «научился», «перестал ошибаться», «стал самостоятельнее» требуют наблюдаемого, разрешённого и контекстного evidence. В P118 ничего такого нет. Field lens поэтому не моделирует человека. Он моделирует границу передачи: неизвестность можно передать честно, а оценку — только после отдельного решения о допустимых inputs.'),
table('Корректирующая передача без ложного происхождения', ['Поле', 'Literal P118', 'Безопасное чтение', 'Запрещённая история'], [
['Review input', '<code>not-provided</code>', 'P118 не получил вход', 'Редактор увидел и одобрил текст'],
['Quality', '<code>not-declared</code>', 'Нет verdict', 'Статья качественная или слабая'],
['Publication', '<code>not-declared</code>', 'Выпуск не заявлен', 'Материал опубликован в декабре 2027'],
['Correction', '<code>not-declared</code>', 'Правка не заявлена', 'Ошибка найдена и исправлена'],
['Outcome', '<code>not-declared</code>', 'Эффект не заявлен', 'Автор или production изменились'],
]),
h2('Петля hand-off не должна замыкаться сама'),
p('В корректирующем процессе обычно рисуют цикл: claim, feedback, revision, release. Для P118 такая схема опасна, если стрелки читаются как произошедшие события. Здесь петля обрывается до первого факта. Она принимает только named question и fixed temporal boundary, затем проверяет отсутствие hidden review inputs и declarations. При любом claimed quality, pass, publication, correction или outcome evaluator выдаёт stop. Он не предлагает исправление и не выбирает адресата.'),
p('Это не отказ от ответственности. Наоборот, hand-off сохраняет ответственность на правильной границе. Будущий owner, если он вообще появится в новом scope, должен решить, нужен ли review, какие данные допустимы, кто увидит материалы и что будет считаться корректирующим действием. P118 не называет owner, потому что назначение роли тоже является действием с последствиями. Предсказать его «для полноты» означало бы создать несуществующую обязанность.'),
p('В поле особенно легко подменить unknown правдоподобной деталью: вставить короткую цитату reviewer, номер revision, дату публикации или пример комментария. Даже synthetic-looking detail будет работать как evidence, если reader не видит её происхождения. Поэтому visual, таблица и code P118 описывают только статусы формы. Они не содержат заголовка будущей статьи, имени человека, оценки формулировки или примера исправленной фразы.'),
figure('/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg', 'Петля корректирующей передачи: временная граница и отсутствие review ведут к hand-off; declared quality, publication, correction или outcome останавливают маршрут.', 'Диаграмма показывает fail-closed форму P118. Она не подтверждает review, feedback, правку, выпуск, развитие автора или производственный эффект.'),
h2('Runnable пример не создаёт correction'),
p('Запуск ниже не открывает процесс и не меняет запись. Код делает только одно: создаёт копию named in-memory literal, проверяет exact values и возвращает поля declarations. Нет обращения к filesystem, network, environment, clock, secrets, telemetry, system или data. Вывод <code>not-declared</code> не является рекомендацией ничего не исправлять; он показывает, что у этого scope нет права утверждать факт исправления.'),
code("import { inspectHandoffLiteral } from './upgrade-2027-12.mjs';\n\nconst handoff = inspectHandoffLiteral();\nconsole.log(handoff.quality, handoff.correction, handoff.outcome, handoff.status);\n// not-declared not-declared not-declared synthetic-plan-hand-off"),
h2('Порядок корректирующей передачи'),
ol(['Оставить claim вопросом до появления разрешённого evidence; не называть его ошибкой заранее.', 'Зафиксировать даты и cutoff вместе с вопросом, чтобы future tense не стал fabricated history.', 'Проверить, что reviewer input, feedback и publication не спрятаны в метаданных, примере или подписи к визуалу.', 'Сохранить quality, pass, publication, correction и outcome как <code>not-declared</code>; не заменять неизвестное оптимистичным pending.', 'Передать только boundary и условия отдельного scope, не назначая автора, reviewer, owner или получателя.', 'Остановить evaluator на unknown source, unknown date, unknown question, hidden input или declared result.']),
h2('Почему развитие автора не является итогом статьи'),
p('Техническое письмо влияет на автора только через цепочку конкретных действий и наблюдений, а не через название раздела. Нужно различать: статья может описывать правило; reviewer может дать комментарий; автор может принять или отклонить его; затем возможна новая версия и отдельная проверка. Каждый переход требует своего evidence и права работать с ним. Склеить цепочку в «развитие» удобно для резюме, но бесполезно для инженерного решения.'),
p('Будущий scope может вообще отказаться от персонального измерения. Возможно, достаточно поправить технический claim, а вопрос об авторе не относится к задаче. Возможно, feedback содержит чувствительные данные и не должен храниться в редакционном артефакте. Возможно, корректировка окажется не нужна, потому что новый источник изменит interpretation. P118 не выбирает между этими вариантами. Его полезность в том, что варианты остаются открытыми и не получают фальшивый prior.'),
p('Такой hand-off также не разрешает задним числом использовать local trajectory как доказательство. Траектория голоса — будущая contract vocabulary: она может подсказать, какие проверки поставить, но не сообщает, что человек уже прошёл эти проверки. Editorial standard — vocabulary критериев, но не timestamp review. Unpublished artefact — не источник подтверждённого эффекта. Каждый из этих объектов может стать входом только после того, как другой scope задаст происхождение, доступ и допустимый вывод.'),
p('Видимый stop — часть заботы о читателе. Если evaluator возвращает <code>stop-hidden-review-input</code>, это не ошибка будущего reviewer. Если возвращает <code>stop-declared-quality-pass-publication-correction-or-outcome</code>, это не оценка качества текста. Stop говорит только о несоответствии границе P118. Такое название не даёт оператору «починить» статус добавлением деталей; правильное действие — убрать неподтверждённый claim либо открыть другой authorised scope.'),
h2('Что передаётся вместе с неизвестностью'),
p('Хорошая передача не оставляет получателю пустой экран. Она даёт точное имя вопроса, даты, source boundary и перечень утверждений, которые пока нельзя делать. Этого достаточно, чтобы будущий scope решил, нужен ли вообще сбор данных. Нельзя добавлять к hand-off предполагаемую причину, потому что она начнёт направлять поиск. Нельзя добавлять готовый verdict, потому что тогда сбор превратится в подтверждение заранее выбранного ответа.'),
p('Для корректировки важен разрыв между ошибкой и следствием. Даже если однажды обнаружится неверный claim, из него не следует, что нужно менять текст, публиковать заметку или оценивать автора. Возможно, claim был ограничен версией; возможно, аудитория не может применить исправление; возможно, источник сам неоднозначен. Новый scope обязан выбирать действие по доступному evidence и риску. P118 не создаёт этот выбор и не подменяет его заранее написанной петлёй revision.'),
p('Полевой документ должен быть устойчив к смене получателя. Если hand-off зависит от конкретного имени, команды или внутренней очереди, он быстро становится историей о людях и требует дополнительных прав. Здесь получатель не назван. Это не недостаток коммуникации: так вопрос можно передать в надлежащий контекст, не создавая ложного обязательства человеку, который не видел исходных материалов и не соглашался их разбирать.'),
p('Отдельно стоит различать correction и deletion. Удаление неподтверждённой фразы может быть правильным действием, но в P118 оно не произошло и не рекомендуется как результат. Пока нет реального текста и входов, нельзя определить, что именно требует изменения. Fixed evaluator охраняет именно эту границу: он не правит строку и не выбирает форму публикации. Его stop — сигнал, что вопрос нельзя завершить данными, которых никто не предоставил.'),
p('Такой режим не делает field lens бесполезным. Он снижает стоимость будущего разбирательства: новый owner не тратит время на развенчание вымышленной chronology, не ищет несуществующий feedback и не объясняет, почему promised correction не выпущена. Вместо этого он получает маленький, точный контракт неизвестности. Если работа будет авторизована, этот контракт можно заменить доказательствами; если нет, он остаётся честным и не создаёт долгов перед людьми или production.'),
h2('Ограничения и следующий шаг'),
p('P118 field не является персональной оценкой, development plan, review log, publication record, correction record, редакционным календарём или production report. Он не утверждает, что текст требует или не требует изменения. Его sources не подтверждают действие конкретного человека. Его SVG не является журналом процесса. The fixture проверяет только известные literals и fail-closed statuses; он не создаёт feedback и не измеряет качественный эффект.'),
p('Следующий шаг допустим лишь там, где новый authorised scope назовёт вопрос, owner, разрешённые inputs, правила защиты данных, метод revision и возможный отказ от correction. Этот scope может принять или закрыть вопрос без действия. Он не должен задним числом приписывать P118 review или publishing history. Завершение P118 остаётся ограниченным: <code>synthetic-plan-hand-off</code> и <code>productionEffect: not-attempted</code>.')
], fieldRefs);
export const revisions = deepFreeze([practice, mechanism, field]);
export function verifyRevisionsAgainstFixture() {
const fixture = runAuthorManifestoFixture();
const articleChecks = revisions.map((item) => { const text = bodyText(item.contentHtml); return text.length >= 10000 && text.length <= 13000 && /(цен[аы]|стоимост|дорог)/i.test(text.slice(0, 1500)) && /<table>/.test(item.contentHtml) && /<figure>/.test(item.contentHtml) && /<pre><code>/.test(item.contentHtml) && /<ol>/.test(item.contentHtml) && /2027-12/.test(text) && /2026-07-31/.test(text) && /productionEffect: not-attempted/.test(text); });
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((item) => [item.slug, bodyText(item.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');