diff --git a/editorial/production/README.md b/editorial/production/README.md index 4c92405..14ca585 100644 --- a/editorial/production/README.md +++ b/editorial/production/README.md @@ -1,6 +1,6 @@ # Производство редакционных партий -На 31 июля 2026 года строгий аудит проходит 259 из 358 созданных материалов. Остальные 99 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. +На 31 июля 2026 года строгий аудит проходит 262 из 358 созданных материалов. Остальные 96 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить. ## Одна партия diff --git a/editorial/reviews/2025-04-draft.md b/editorial/reviews/2025-04-draft.md new file mode 100644 index 0000000..71548d5 --- /dev/null +++ b/editorial/reviews/2025-04-draft.md @@ -0,0 +1,110 @@ +# P86 · 2025-04 · Данные и приватность в AI-инструментах — три прохода ревью + +## Граница самостоятельного draft-пакета + +- Overlay затрагивает только три April 2025 slug через web/scripts/upgrade-2025-04.mjs. +- Все context records, labels, authorization, cases, citations и outcomes — versioned fixed synthetic JavaScript literals в памяти. В strings нет customer code, PII, secret, реального лога, тикета, source file или внешнего идентификатора. +- Скрипт не выполняет prompt/model call, external request, filesystem, Git, CI, network, telemetry, clock, production action или иной side effect. Положительный outcome означает только synthetic hand-off; egressPerformed и productionEffect остаются false / not-attempted. +- Registry, README, очередь, articles.json, app-страницы, full production build, staging, commit и push намеренно не входят в этот пакет. + +## База исследования и историческая граница + +Все четыре ссылки ниже проверены обычным HTTPS GET без TLS bypass и без авторизации: каждая вернула HTTP 200. Два mutable GitHub Docs источника закреплены immutable commit от 30 апреля 2025; NIST публикации ведут на final page или датированный final PDF. Тексты не делают общий юридический вывод и не переносят terms одного service на другой. + +| Источник | Версия / дата | Узкое утверждение в статьях | Явная граница | +| --- | --- | --- | --- | +| [GitHub Docs: Responsible use of GitHub Copilot Chat in GitHub](https://github.com/github/docs/blob/0d17a12af77011b390abd8aea9e8626e153e0522/content/copilot/responsible-use-of-github-copilot-features/responsible-use-of-github-copilot-chat-in-github.md) | immutable commit 0d17a12af77011b390abd8aea9e8626e153e0522, 30.04.2025 | Prompt предварительно обрабатывается и сочетается с context; конкретная surface может получить repository context, а при включённом Bing сформированный query передаётся Bing Search API. | Это описание одной surface на фиксированной ревизии. Оно не классифицирует данные, не фиксирует retention/training для другой организации и не разрешает передачу сотруднику. | +| [GitHub Docs reusable: Copilot SKU isolation](https://github.com/github/docs/blob/0d17a12af77011b390abd8aea9e8626e153e0522/data/reusables/copilot/sku-isolation.md) | immutable commit 0d17a12af77011b390abd8aea9e8626e153e0522, 30.04.2025 | Firewall allow-list может разрешать Business/Enterprise plan endpoint, а block-list — запрещать Individual endpoint в корпоративной сети. | Это endpoint-level routing control, не inspection payload. Он не доказывает class, contract/retention, training terms или human authorization. | +| [NIST SP 800-207: Zero Trust Architecture](https://csrc.nist.gov/pubs/sp/800/207/final) | final, 11.08.2020 | Network location не даёт implicit trust; authentication и authorization — отдельные policy-based функции для resource access. | Это security architecture, не DPA и не локальная классификация данных. Документ не описывает AI vendor routing, storage или конкретный service scope. | +| [NIST AI 100-1: AI RMF 1.0](https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.100-1.pdf) | final PDF, January 2023 | AI RMF связывает GOVERN, MAP, MEASURE и MANAGE; privacy рассматривается вместе с организационными и техническими практиками управления риском. | Добровольная рамка не делает юридический вывод, не назначает owner и не заменяет vendor contract, egress measurement или access check. | + +Историческая оговорка: в пакет не добавлены поздние model names, product modes, routing claims, provider terms или agent features после апреля 2025. Перед реальным использованием документация, contract и конфигурация должны быть проверены заново для своего plan, surface, region, account и destination. + +## Проход 1 — факты, историческая граница и техника + +Проверены все утверждения о внешних системах против таблицы выше. В статьях использованы только узкие факты: потенциальное расширение context на конкретной GitHub surface, plan-specific endpoint isolation и принцип policy-based authorization. Ни один источник не используется как доказательство фактического хранения, обучения, region, subprocessor, legal basis или допустимости конкретного фрагмента. + +Техническая модель разделяет четыре решения: + +1. dataClass и allowability описывают локальный fixed record. +2. retentionLabel обозначает наличие отдельного fixed contract-condition, но не утверждает реальное vendor behavior. +3. egressScope сопоставляет record и symbolic destination, но не является hostname или proxy configuration. +4. authorization содержит issuer role, record scope, requester labels, date range, reason и citation; она не может переопределить deny-class. + +Граница безопасности формы проверяется отрицательными cases: extra/missing keys, forged authorization и расширенный scope, requester access mismatch, expiry, другой egress scope, sparse arrays и cyclic values. Любое расхождение создаёт closed decision и safe stop без payload. Even accepted case заканчивается до внешнего вызова. + +~~~text +node --check scripts/upgrade-2025-04.mjs +PASS + +node scripts/upgrade-2025-04.mjs --verify-fixture +PASS fixture: 28/28 assertions +~~~ + +Ограничение прохода: fixture доказывает только contract fixed in-memory модели. Она не тестирует model provider, firewall, DLP, identity system, actual network, document retention, human action или production access. + +## Проход 2 — редактура, M8 и усиленный draft audit + +Проверены первые два абзаца каждой статьи: они называют наблюдаемую проблему и цену ошибки — утрату проверяемой цепочки class → contract → egress → authority, повторную инвентаризацию и остановку решения. В текст не добавлены вымышленные инциденты, деньги или утечки; field case остаётся synthetic. + +Тон соответствует уровню М8: автор не обещает «безопасный prompt», сравнивает scope доказательств, разделяет control layers и оставляет короткий воспроизводимый процесс. Связка «симптом → причина → проверка → действие» выдержана в practice, mechanism и field case. Технические термины привязаны к record, label, destination, authorization или expiry, а не используются как общая оценка. + +| Статья | Основной текст | Содержание, проверенное редактурой | +| --- | ---: | --- | +| Practice | 8 227 знаков | Карточка контекста, distinction class/contract/egress/authority, in-memory example, pre-flight order и next step. | +| Mechanism | 9 014 знаков | Матрица доказательств, fail-closed порядок, scope retention label, zero-trust boundary и four-column evidence sheet. | +| Field | 10 247 знаков | Synthetic log/ticket case, scoped authorization, negative path, role split, concrete review order и tabletop next step. | + +Повторный усиленный audit после добавления формальной последовательности действий в field case: + +~~~text +npm run audit:draft -- scripts/upgrade-2025-04.mjs +PASS editorial-2025-04-practice-ai-data-privacy: 8227 body chars +PASS editorial-2025-04-mechanism-ai-data-privacy: 9014 body chars +PASS editorial-2025-04-field-ai-data-privacy: 10247 body chars +~~~ + +Audit также проверяет пять смысловых разделов, таблицу с thead, figure/caption/meaningful alt, code example, ordered sequence, две внешние ссылки, стартовую постановку проблемы и цену ошибки в первых двух paragraphs. Сначала field article не прошла ровно по отсутствию ordered sequence; добавлен отдельный краткий review order, после чего полный audit повторён и прошёл. + +Ограничение прохода: объём, структура и ясность терминов не подтверждают применимость source или policy к чужому service. Они лишь делают неизвестный слой заметным и требуют owner question. + +## Проход 3 — SVG, fixture и выпускная граница + +Каждая статья получает отдельный SVG с осмысленным alt и caption: + +| Asset | Sharp render 375 px | Ручная проверка | +| --- | --- | --- | +| ai-data-privacy-2025-context-boundary.svg | 375 × 513 | Карточки class, contract/retention, egress и authority читаемы; красная safe-stop ветка не обрезана. | +| ai-data-privacy-2025-allowability-matrix.svg | 375 × 523 | Четыре synthetic rows, колонки evidence и итоговая формула различимы без горизонтального overflow. | +| ai-data-privacy-2025-egress-review.svg | 375 × 493 | Пять review steps, stop branch и финальная граница no external request читаемы на узком экране. | + +~~~text +xmllint --noout <3 SVG> +PASS + +SVG safety scan for script, foreignObject, javascript:, data:image and event handlers +PASS + +Sharp 375 px + manual visual review +PASS +~~~ + +Первый narrow-screen render выявил переполнение длинных строк в context-boundary.svg и egress-review.svg. Текст разбит на короткие строки, SVG снова прошли XML/safety checks и повторный Sharp/manual review. Матрица не требовала правки. + +Полная production build, registry update, staging, commit и push сознательно не запускались: они запрещены scope самостоятельного P86 draft-пакета. До будущей публикации нужен отдельный интеграционный выпускной проход, который подключит overlay к registry, повторит audit на собранных статьях и проверит полный build. + +## Выпуск после независимой приёмки + +### 1. Факты и техническая граница + +Редактор заново прочитал все три текста и проверил первичные источники обычным HTTPS без авторизации и обхода TLS. GitHub Docs закреплён commit `0d17a12af77011b390abd8aea9e8626e153e0522` от 30 апреля 2025 года: raw-файлы подтверждают предобработку prompt с context, дополнительный repository/Bing context и plan-specific firewall isolation. Официальная страница NIST SP 800-207 подтверждает отсутствие implicit trust по network location и раздельные authentication/authorization; final PDF NIST AI RMF 1.0 подтверждает функции GOVERN, MAP, MEASURE и MANAGE. В статьях эти факты не расширены до вывода о конкретном vendor, хранении, обучении, договоре или юридической допустимости. + +Проверены `node --check`, 28 из 28 fixture assertions и усиленный draft audit. Основной текст статей содержит соответственно 8 227, 9 014 и 10 247 знаков; в первых двух абзацах каждой названы ситуация и цена неполного evidence. Форма примеров намеренно ограничена fixed synthetic records: positive outcome остаётся hand-off, а `egressPerformed` не становится фактической отправкой. + +### 2. Редактура и голос автора + +Полностью вычитаны practice, mechanism и field case. У каждой есть постановка проблемы, различение class/policy-contract/egress/authority, таблица, схема, воспроизводимый отрицательный или положительный synthetic example, последовательность проверки, граница применимости и следующий проверяемый шаг. Термины привязаны к конкретному record и не используются как рекламная оценка инструмента. Тон М8 остаётся прагматичным: «симптом → причина → проверка → действие» важнее обещания безопасного prompt. + +### 3. Визуальная и выпускная проверка + +Все три SVG прошли XML и safety scan; для каждого выполнен Sharp render на ширине 375 px и ручная проверка. Карточки, матрица и маршрут review читаются без обрезания или горизонтального переполнения; у каждого рисунка есть содержательные `alt` и подпись. Overlay подключён к общему registry. До коммита выполнены целевой audit собранных статей, проверка уникальности slug, `git diff --check` и полная production build. diff --git a/web/data/editorial-revisions.mjs b/web/data/editorial-revisions.mjs index 8a116fd..34192d7 100644 --- a/web/data/editorial-revisions.mjs +++ b/web/data/editorial-revisions.mjs @@ -82,6 +82,7 @@ import { revisions as december2024Revisions } from '../scripts/upgrade-2024-12.m import { revisions as january2025Revisions } from '../scripts/upgrade-2025-01.mjs'; import { revisions as february2025Revisions } from '../scripts/upgrade-2025-02.mjs'; import { revisions as march2025Revisions } from '../scripts/upgrade-2025-03.mjs'; +import { revisions as april2025Revisions } from '../scripts/upgrade-2025-04.mjs'; // This layer replaces archived source entries without losing their stable slug and date. export const editorialRevisions = [ @@ -169,4 +170,5 @@ export const editorialRevisions = [ ...january2025Revisions, ...february2025Revisions, ...march2025Revisions, + ...april2025Revisions, ]; diff --git a/web/public/assets/editorial/2025/ai-data-privacy-2025-allowability-matrix.svg b/web/public/assets/editorial/2025/ai-data-privacy-2025-allowability-matrix.svg new file mode 100644 index 0000000..e8f7382 --- /dev/null +++ b/web/public/assets/editorial/2025/ai-data-privacy-2025-allowability-matrix.svg @@ -0,0 +1,55 @@ + diff --git a/web/public/assets/editorial/2025/ai-data-privacy-2025-context-boundary.svg b/web/public/assets/editorial/2025/ai-data-privacy-2025-context-boundary.svg new file mode 100644 index 0000000..5e4b17e --- /dev/null +++ b/web/public/assets/editorial/2025/ai-data-privacy-2025-context-boundary.svg @@ -0,0 +1,58 @@ + diff --git a/web/public/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg b/web/public/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg new file mode 100644 index 0000000..e627568 --- /dev/null +++ b/web/public/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg @@ -0,0 +1,47 @@ + diff --git a/web/scripts/upgrade-2025-04.mjs b/web/scripts/upgrade-2025-04.mjs new file mode 100644 index 0000000..d334dd2 --- /dev/null +++ b/web/scripts/upgrade-2025-04.mjs @@ -0,0 +1,863 @@ +function escapeHtml(value) { + return String(value) + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", '''); +} + +const p = (text) => '
' + text + '
'; +const h2 = (text) => '' + escapeHtml(text) + '';
+const ol = (items) => '| ' + item + ' | ').join('') + '
|---|
| ' + item + ' | ').join('') + '
synthetic-public в учебной модели всё равно требует policy/contract label и отдельного разрешения владельца. И наоборот, даже корректный человек не получает права поднять synthetic-restricted до допустимого класса одной подписью. Такое разделение неприятно в первый день, потому что прибавляет несколько полей. Зато на следующем review не приходится угадывать, относим ли мы обсуждение к содержимому, маршруту или полномочиям.'),
+ figure('/assets/editorial/2025/ai-data-privacy-2025-context-boundary.svg', 'Пять независимых границ перед AI-инструментом: class данных, допустимость и retention, technical egress, human authorization и hand-off без реальной отправки.', 'Схема показывает, что отсутствие доказательства на любом шаге ведёт к safe stop. Зелёный hand-off означает только готовность передать вопрос владельцам, а не выполненный внешний запрос.'),
+ h2('Минимальная карточка: какие поля отвечают на какие вопросы'),
+ table('Поля fixed synthetic record', ['Поле', 'Проверяемый вопрос', 'Пример label', 'Чего поле не доказывает'], [
+ ['dataClass', 'Какой локальный класс у содержимого?', 'synthetic-public или synthetic-restricted', 'условия vendor contract и сетевой маршрут'],
+ ['allowability', 'Можно ли вообще рассматривать egress?', 'allow-after-human-authorization', 'что разрешение уже выдано конкретному requester'],
+ ['retentionLabel', 'Есть ли проверенное условие хранения/обучения для нужного scope?', 'synthetic-contract-retention-reviewed', 'фактический payload, endpoint или будущую версию сервиса'],
+ ['egressScope', 'Какой symbolic destination разрешён карточке?', 'fixed-external-ai-boundary-alpha', 'что firewall действительно пропустил данные или что payload безопасен'],
+ ['authorityRequired и expiry', 'Кто и до какой даты вправе одобрить hand-off?', 'synthetic-data-steward до 20 апреля', 'права всех будущих пользователей или другой record'],
+ ['citation', 'На какую карточку и решение можно сослаться?', 'synthetic://p86/…', 'доказательство реальной политики вне учебной модели'],
+ ]),
+ p('В реальном процессе поля могут жить в каталоге данных, тикете, policy-as-code или системе заявок. Место не важно для этой статьи; важна связь. Если class записан в одном месте, contract в другом, egress в третьем, а approval лежит в личной переписке, review не может собрать их в один проверяемый chain. Тогда короткий prompt экономит минуту, но создаёт дорогой вопрос «почему мы решили, что это допустимо?».'),
+ h2('Воспроизводимый пример: только фиксированные записи в памяти'),
+ p('Ниже не показан запрос к модели и не передаётся даже синтетический payload. Скрипт создаёт заранее заданную карточку, проверяет равенство class, contract label, egress scope и dated authorization, а затем возвращает decision. Даже положительный outcome заканчивается на hand-off: поле egressPerformed остаётся false. Такая форма полезна для обучения review, потому что нельзя случайно выдать пример за интеграцию.'),
+ code([
+ "import {",
+ " createFixedSyntheticContextRequest,",
+ " assessSyntheticAiEgress,",
+ " stopSyntheticAiEgress,",
+ "} from './upgrade-2025-04.mjs';",
+ '',
+ "const request = createFixedSyntheticContextRequest('authorized-public-summary');",
+ 'const decision = assessSyntheticAiEgress(request);',
+ 'const boundaryStop = stopSyntheticAiEgress(decision);',
+ '',
+ 'console.log(decision.accepted); // true: fixed hand-off only',
+ 'console.log(decision.egressPerformed); // false',
+ 'console.log(boundaryStop.stopped); // true: no external request exists',
+ ].join('\n')),
+ p('У модели есть и отрицательные cases: internal record без settled contract label, restricted log shape, requester без нужного access label, expired authorization и другой egress scope. Каждый возвращает reason, citation, stop condition и next action. Важна формулировка stop: «на этом evidence нельзя открыть путь», а не «данные навсегда запрещены». Внешний мир может содержать дополнительные договоры и технические меры; эта модель просто не имеет права их придумывать.'),
+ h2('Как проводить короткий pre-flight review'),
+ ol([
+ 'Назовите единицу. Запишите record id, version и class до того, как копировать фрагмент в prompt или подключать инструмент к репозиторию.',
+ 'Разведите policy и contract. Сверьте allowability с нужным retention/training condition для данного service scope; не переносите вывод с другого plan или старой версии.',
+ 'Проверьте egress отдельно. Сопоставьте destination, route и технический allow-list. Наличие сети или лицензии не делает payload допустимым.',
+ 'Сверьте access и authority. Requester должен иметь доступ к record, а владелец — выдать scoped, dated решение. Approval без scope не переносится на соседний фрагмент.',
+ 'Запишите safe stop. Если один факт неизвестен, оставьте payload локально, укажите owner question и не ищите обход через другой UI или account.',
+ ]),
+ h2('Почему интерфейс и договор не закрывают весь маршрут'),
+ p('Фиксированный GitHub Docs на апрель 2025 показывает полезную инженерную деталь: prompt может обрабатываться вместе с контекстом, а отдельные возможности могут собирать repository context или сформировать Bing query при включённом поиске. Это не утверждение о каждом AI-инструменте. Оно показывает, почему проверять только видимый кусок текста недостаточно: нужно знать, какая поверхность и какие дополнительные источники контекста включены в данном режиме.'),
+ p('Другой GitHub документ описывает SKU isolation через plan-specific endpoint в firewall. Это хороший пример технического control: он может ограничить, какой plan endpoint используют люди в сети. Но он не видит data class, не сравнивает условие retention, не проверяет, что человек вправе раскрыть record, и не заменяет решение владельца. NIST SP 800-207 так же отделяет policy-based access от одного лишь network location. В статье это не юридическая консультация, а причина не считать один control доказательством всех остальных.'),
+ h2('Ограничения и следующий проверяемый шаг'),
+ p('Весь пример — versioned fixed synthetic JavaScript literals в памяти. Здесь нет customer code, PII, secret, реального лога, тикета, файла, Git, CI, telemetry, сети, clock, model call или production side effect. Labels synthetic-public, synthetic-restricted и synthetic-contract-retention-reviewed не описывают ни одну вашу систему и не доказывают фактическое хранение или обучение у поставщика. Они нужны только для проверки формы решения и fail-closed пути.'),
+ p('Следующий шаг: возьмите один тип контекста, который команда реально хочет использовать с AI, но не копируйте его в шаблон. Сначала создайте пустую карточку с class, allowability, contract/retention evidence, egress scope, owner, expiry и citation. Если хотя бы одно поле нельзя честно заполнить, результат review уже полезен: это safe stop и конкретный вопрос к data, security, legal или service owner.'),
+ h2('Историческая граница апреля 2025'),
+ p('Здесь используются только источники, существовавшие к 30 апреля 2025: два immutable GitHub Docs commit, NIST SP 800-207 final 2020 и NIST AI RMF 1.0 final 2023. В текст не перенесены поздние model names, provider terms или функции агентов. Любая будущая проверка должна заново закрепить версию документа и применимость к своему plan, region, contract и маршруту.'),
+]);
+
+const mechanism = revision({
+ slug: 'editorial-2025-04-mechanism-ai-data-privacy',
+ title: 'Четыре разных доказательства для AI-egress',
+ excerpt: 'Почему class, policy/contract, technical egress и human authorization должны сходиться на одной версии контекста, а не заменять друг друга.',
+ readingMinutes: 12,
+}, [
+ p('Самая дорогая ошибка в работе с AI-контекстом часто происходит без утечки, сбоя или громкого инцидента. Команда уже не может восстановить решение: был ли фрагмент разрешён, к какому plan он относился, куда шёл трафик и кто согласовал срок. Цена ошибки — остановка внедрения, повторная инвентаризация и риск принять второе решение по памяти вместо фактов. Особенно плохо, когда evidence распределён между настройкой IDE, DPA, сетевым правилом и устным «можно».'),
+ p('Причина — попытка сделать один артефакт универсальным. Classification не равна policy, policy не равна contract, contract не равен egress, egress не равен authorization. Они пересекаются только в том, что все относятся к одной передаче. М8-автор не предлагает «самую строгую» кнопку: он строит короткую цепочку, в которой каждое звено отвечает на свой вопрос и честно оставляет границу видимой.'),
+ h2('Разложить решение на четыре оси'),
+ p('Data classification описывает содержимое локальным языком команды: public, internal, restricted, personal-data-shape или другой явно определённый class. Это вход в решение, а не вывод о поставщике. Policy задаёт допустимость: например, class может требовать отдельного review или прямо закрывать внешнюю передачу. Contract уточняет, какие обязательства проверены для выбранного service scope: retention, processing, training terms, region или subprocessor — ровно те, которые важны организации. Нельзя заменить неизвестное условие словом «корпоративный».'),
+ p('Technical egress отвечает уже на другой вопрос: есть ли путь к конкретному destination и соответствует ли он разрешённой конфигурации. Firewall allow-list, proxy rule, DNS policy или network segmentation полезны, но они работают с route и endpoint, а не со смыслом фрагмента. Human authorization отвечает на третий слой принятия решения: какой owner рассмотрел record, requester, destination и срок. Владелец не должен переопределять deny-class молча, а сеть не должна доказывать его полномочия.'),
+ figure('/assets/editorial/2025/ai-data-privacy-2025-allowability-matrix.svg', 'Матрица четырёх synthetic context record показывает, что class, policy/contract, egress и authority дают разные outcomes: hand-off только у одного fixed public case, остальные ведут в stop.', 'Матрица не классифицирует реальные данные. Она делает заметным, почему положительный факт в одной колонке не переносится в соседнюю.'),
+ h2('Матрица доказательств: какой артефакт имеет какой scope'),
+ table('Не путать proof с похожим сигналом', ['Вопрос', 'Нужное evidence', 'Что может помочь', 'Чего этого недостаточно доказать'], [
+ ['Какой class у record?', 'версионированная карточка и owner vocabulary', 'название папки или тега', 'допустимость внешней передачи'],
+ ['Допустим ли class?', 'policy decision и contract condition для service scope', 'DPA или UI setting как вход в review', 'фактический destination и применимость к конкретному plan'],
+ ['Куда может пойти трафик?', 'проверенный destination, route и egress control', 'firewall allow-list', 'что payload соответствует class или что contract покрывает usage'],
+ ['Кто разрешил передачу?', 'dated authorization с record, requester и scope', 'лицензия пользователя или одобрение продукта', 'права на другой record, срок или destination'],
+ ['Что делать при пробеле?', 'reason, citation, stop condition и owner question', 'общее «надо уточнить»', 'обход через другой account, device или tool'],
+ ]),
+ p('Это не бюрократическая таблица ради таблицы. Она сокращает конфликт review. Когда security говорит «endpoint разрешён», а privacy отвечает «record не классифицирован», оба могут быть правы, просто отвечают на разные вопросы. Правильное действие — не голосовать, чей control сильнее, а сохранить два факта и закрыть egress до тех пор, пока policy/contract факт не появится. Так stop остаётся техническим решением о неполном evidence, а не личной оценкой осторожности коллеги.'),
+ h2('Механизм fail-closed: сначала проверки, затем только hand-off'),
+ p('В P86 одинаково важны порядок и отрицательный результат. Модель сначала проверяет exact keys и canonical form request, потом вытаскивает только known fixed records. Далее она оценивает allowability и retention label, сравнивает egress scope, access label и датированную authorization. Нельзя начать с approval: иначе оно станет способом скрыть restricted class. Нельзя начать с network: иначе успешный route станет ложным доказательством допустимости.'),
+ code([
+ "const restricted = createFixedSyntheticContextRequest('restricted-class-stop');",
+ 'const decision = assessSyntheticAiEgress(restricted);',
+ 'const stop = stopSyntheticAiEgress(decision);',
+ '',
+ 'console.log(decision.classificationDecision);',
+ '// denied-by-fixed-class-or-allowability',
+ 'console.log(decision.authorizationDecision);',
+ '// not-consulted-as-an-override',
+ 'console.log(stop.egressPerformed);',
+ '// false',
+ ].join('\n')),
+ p('Низкий уровень реализации здесь нарочно скучный. Record ids, authorization и expected fields не принимаются «похожими». Лишний key, missing key, sparse array, cyclic value или forged authorization не получают частичный accept: fixture возвращает closed decision. Это защищает учебный пример от обычной ошибки автоматизации, когда сериализатор или удобный spread silently добавляет неоговорённую информацию. В production нужны другие controls, но fail-closed форма полезна уже до выбора tool.'),
+ h2('Retentions и training terms — отдельная проверка, не магический label'),
+ p('Retention label в примере называется synthetic-contract-retention-reviewed. Он не утверждает, что какой-либо vendor реально не хранит prompt, не обучается на нём или не передаёт его subprocessors. Он говорит гораздо уже: для одной synthetic карточки в учебной модели существует проверяемое условие, без которого hand-off закрыт. В реальной работе label должен ссылаться на документ и версию, applicable plan, region, accounts, features и исключения. Если термины меняются, изменяется не только ссылка, но и вывод.'),
+ p('Именно здесь полезна дисциплина immutable sources. GitHub Docs на закреплённом commit фиксирует, что конкретная chat surface может сочетать user input с дополнительным context и при включённом Bing отправлять сформированный query в Bing Search API. Это не доказывает retention или training для вашего продукта, но помогает задать правильный вопрос: какой контекст собирает именно этот surface и есть ли дополнительные outbound paths? Необходимо проверять не только marketing page, а технический режим, который включён команде.'),
+ h2('Egress control и zero trust: полезный, но ограниченный слой'),
+ p('GitHub SKU isolation описывает plan-specific endpoint и firewall allow/block list. Такой control может снизить риск того, что сотрудник в корпоративной сети использует не тот subscription path. Но данные не приобретают класс в момент TLS connection. Поэтому в таблице egress gate идёт после class и contract, а не вместо них. Если proxy разрешил host, остаются вопросы: какой feature включён, какой context он добавляет, соответствует ли record нужному scope и есть ли human authorization на этот date.'),
+ p('NIST SP 800-207 формулирует эту мысль шире: network location сама по себе не даёт implicit trust, а access к resource опирается на policy и отдельные authentication/authorization functions. В этой статье AI-инструмент не объявляется enterprise resource из NIST и не переносится схема стандарта один к одному. Важен принцип разделения: факт «путь существует» не отвечает на вопрос «данный субъект вправе передать данный record по этому пути».'),
+ h2('Порядок review, который можно повторить'),
+ ol([
+ 'Соберите одну строку identity. Record id, version, class и owner должны быть видны до обсуждения prompt.',
+ 'Спросите policy/contract question. Какое условие допустимости и retention/training evidence нужно именно для этой surface, plan и destination?',
+ 'Спросите egress question. Какая техническая граница разрешена и кто подтвердил её применимость, не подменяя этим class?',
+ 'Спросите authority question. Кого достаточно для решения, какой requester и date покрыты, а какие нет?',
+ 'Сделайте один outcome. Hand-off в отдельный authorised workflow или safe stop с reason, citation и следующим владельцем. Не делайте send в ходе review.',
+ ]),
+ h2('Ограничения и следующий проверяемый шаг'),
+ p('Ни матрица, ни fixture не являются privacy impact assessment, договором или архитектурой DLP. Они не обнаруживают PII, не проверяют endpoint, не читают logs, не знают region, не вызывают model и не оценивают legal basis. В фиксированных strings намеренно нет customer data, secret, исходного кода, реального тикета или telemetry. Поэтому положительный synthetic outcome нельзя превращать в разрешение на любой настоящий prompt, а отрицательный нельзя использовать как общий запрет на технологию.'),
+ p('Следующий шаг: для одного включённого AI feature создайте four-column evidence sheet: class/owner, policy+contract, egress, authorization. В каждой колонке оставьте URL или immutable reference, date и boundary: что этот факт не доказывает. Затем возьмите один известный gap и проверьте, что система безопасно останавливается без доступа к payload. Результат должен быть короче policy memo, но достаточен, чтобы другой reviewer повторил вопрос.'),
+ h2('Историческая граница апреля 2025'),
+ p('Пакет опирается на GitHub Docs commit от 30 апреля 2025 и на финальные NIST публикации 2020 и 2023. Он не предполагает, что поведение, storage, model routing или product terms позднее не изменятся. При реальном внедрении документация поставщика, договор и конфигурация проверяются заново на дату решения; эта статья показывает форму проверки, а не переносимый verdict.'),
+]);
+
+const field = revision({
+ slug: 'editorial-2025-04-field-ai-data-privacy',
+ title: 'Когда synthetic log shape должен остановить AI-review',
+ excerpt: 'Кейс без реальных логов и PII: как пройти class, contract, egress, access и expiry до того, как AI-инструмент увидит контекст.',
+ readingMinutes: 12,
+}, [
+ p('Во время разбора ошибки инженеру хочется показать AI-инструменту «всего один лог и один тикет»: так легче получить гипотезу о причине. Но если у фрагмента нет class, нельзя сказать, допустим ли сам вопрос, а не только удобен ли ответ. Цена ошибки — потеря control над цепочкой: в следующем review никто не отличит разрешённый public example от restricted event shape, не увидит destination и не сможет доказать, что approval действовал в тот момент.'),
+ p('В этом кейсе нет настоящего лога, клиента, идентификатора, секретного значения, файла или сетевого запроса. Есть только fixed synthetic records с нарочно скучными placeholder. Это важно: пример учит остановить передачу до egress, а не демонстрирует «безопасный способ» отправить production context. Симптом — кто-то хочет дать ассистенту debugging material. Причина — четыре решения слились в просьбу «разрешите prompt». Проверка и действие разложены дальше по шагам.'),
+ h2('Case 1. Class звучит как «log», но decision зависит от содержимого и rule'),
+ p('Первый record называется synthetic-restricted-log-shape-v1. Его payload не содержит события: это строка с symbolic labels actor=[symbolic label], token=[not-present] и request=[not-present]. Всё равно record получает class synthetic-restricted, allowability deny-external-egress и retention label synthetic-no-external-retention. Название «shape» не пытается спрятать риск; наоборот, заставляет review решить, нужен ли реальный schema owner до обсуждения материала.'),
+ p('Симптом: «мы же удалили значения, значит фрагмент можно передать». Причина: redaction, class и contract — разные операции. Проверка: спросить, кто утвердил class именно после преобразования, что осталось в structure, какие условия обработки применимы и есть ли evidence для конкретного tool surface. Действие: если class или allowability не доказаны, хранить фрагмент локально и открыть owner question. Нельзя поднять deny-class тем, что reviewer уверен в хороших намерениях автора.'),
+ h2('Case 2. Egress gate закрыт даже при знакомом продукте'),
+ p('В model egressScope — символическая строка fixed-external-ai-boundary-alpha. Она не является hostname, proxy configuration или реальным endpoint. Её задача — показать правило сопоставления: record, request и authorization должны назвать один scope. Если request просит fixed-unapproved-egress-scope, decision закрывается до проверки human authorization. Это предотвращает подмену «у человека есть approval» на «он может выбрать любой путь».'),
+ p('Симптом: команда знает, что продукт закуплен, и поэтому считает любой его UI одинаковой границей. Причина: plan, surface, подключённый search, extension и route могут иметь разный context flow. Проверка: выписать destination как отдельную сущность и найти техническое evidence для этого режима. Действие: если destination не совпал с record scope, вернуть safe stop с reason и citation. Не искать удобный альтернативный account или device: он меняет путь, но не закрывает policy gap.'),
+ figure('/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg', 'Вертикальный маршрут review: record/class, policy/contract, egress, access/authorization и decision с citation; любое mismatch ведёт в safe stop без передачи наружу.', 'Последовательность отделяет evidence от действия. Даже зелёный hand-off остаётся внутри fixed synthetic model и не выполняет external request.'),
+ h2('Case 3. Authorization имеет scope, access и expiry'),
+ p('В положительном fixed case approval принадлежит роли synthetic-data-steward, покрывает только synthetic-public-interface-summary-v1, только fixed-external-ai-boundary-alpha, requester label synthetic-engineering-read и заканчивается 20 апреля 2025. Он не покрывает restricted log shape и не действует после срока. Такая точность может казаться излишней для одного фрагмента, но без неё authorization становится переносимой печатью «можно AI».'),
+ p('Симптом: у человека есть старое одобрение, но никто не сверил requester и date. Причина: approval считают свойством tool или команды, а не решением для конкретного resource. Проверка: сопоставить record ids, destination, requester labels, issuer role, validFrom и expiresAt. Действие: при любом mismatch model возвращает safe stop; новый owner должен принять новое решение на свежем evidence. Не изменяйте expiry в старой карточке задним числом: это убирает историю, которая нужна следующему reviewer.'),
+ table('Review record для одного synthetic request', ['Шаг', 'Наблюдение', 'Decision в модели', 'Следующее действие'], [
+ ['Class', 'log-shape имеет synthetic-restricted и deny-external-egress', 'classification denied', 'не передавать payload; спросить owner о допустимой учебной замене'],
+ ['Contract', 'retention label — synthetic-no-external-retention', 'contract not accepted', 'не выводить условия vendor по аналогии с другим plan'],
+ ['Egress', 'scope request не совпадает с record', 'gate closed', 'уточнить destination/route у network и service owner'],
+ ['Access', 'requester не имеет required label', 'authorization mismatch', 'проверить право на record без расширения scope в prompt'],
+ ['Expiry', 'approval закончился до requestedAt', 'safe stop', 'запросить новое dated решение или отказаться от передачи'],
+ ]),
+ h2('Воспроизводимый negative path без внешнего контекста'),
+ p('В коде ниже case уже содержит fixed record id и fixed authorization. Он не читает лог, не строит prompt и не делает HTTP call. Именно поэтому его можно запускать как fixture: мы проверяем, что denied class закрывается до approval override, а safe stop не выдаёт payload. В настоящей системе этот пример не заменяет data inventory или access control; он даёт небольшой контракт для теста: если доказательства нет, функция не открывает путь.'),
+ code([
+ "const request = createFixedSyntheticContextRequest('restricted-class-stop');",
+ 'const decision = assessSyntheticAiEgress(request);',
+ 'const stop = stopSyntheticAiEgress(decision);',
+ '',
+ 'console.log(decision.accepted); // false',
+ 'console.log(decision.classificationDecision); // denied-by-fixed-class-or-allowability',
+ 'console.log(decision.nextAction); // owner question, no payload copy',
+ 'console.log(stop.productionEffect); // not-attempted',
+ ].join('\n')),
+ p('Fixture добавляет более жёсткие границы, чем happy path. Extra key и missing key не проходят exact contract. Forged authorization и расширенный scope не совпадают с fixed request proof. Sparse array и cyclic value не становятся «пустым списком», а закрываются без исключения. Отдельные cases проверяют requester access, authorization expiry и wrong egress scope. Это не антифрод и не DLP; это проверка, что сам учебный gate не переходит от отсутствующих фактов к неявному разрешению.'),
+ h2('Как оформить human review без ложного юридического вывода'),
+ p('Вопрос владельцу должен быть конкретнее, чем «можно ли использовать AI?». Например: «для record class X, surface Y и destination Z: какое allowability rule действует, каким immutable document подтверждается retention/training condition, кто является authority, какой requester и expiry покрыты?» Такой вопрос не утверждает, что DPA сам по себе разрешает передачу или что UI setting гарантирует место обработки. Он просит evidence, по которому организация вправе сделать собственный вывод.'),
+ p('Полезно разделить роли. Data owner подтверждает class и преобразование. Service or legal owner сопоставляет policy/contract с выбранной surface. Network owner подтверждает path и egress control. Security or privacy owner проверяет access and authorization process. Один человек может совмещать роли в небольшой компании, но в review всё равно стоит записать, какой вопрос он закрыл. Это снижает риск «одобрения вообще» и позволяет вернуть только спорный слой на доработку.'),
+ h2('Что дают источники, а чего они не дают'),
+ p('GitHub Docs на commit 30 апреля 2025 говорит, что в конкретной Copilot Chat surface prompt может обрабатываться вместе с context, а Bing search при включении отправляет сформированный query в Bing Search API. Это поддерживает постановку вопроса о расширенном context и отдельной внешней границе. Документ SKU isolation показывает пример endpoint-level firewall control. Он не описывает class конкретного лога, не подтверждает retention или training terms организации и не назначает человека, который может выдать approval.'),
+ p('NIST SP 800-207 поддерживает принцип явного, policy-based access вместо доверия к network location; NIST AI RMF помещает privacy и governance в постоянное управление риском. Оба источника не являются DPA, конфигурацией proxy или локальным classification policy. Поэтому они не используются для вывода «можно передавать». Их роль — удержать структуру решения: resource, scope, control, authority и documented risk должны быть видны раздельно.'),
+ h2('Короткая последовательность для реального review'),
+ ol([
+ 'Остановите копирование. Сначала назовите record и class, не открывая AI surface и не отправляя сокращённый «пример».',
+ 'Проверьте допускаемость. Сведите policy и contract evidence к выбранным plan, feature, destination и сроку, а не к бренду поставщика.',
+ 'Сверьте путь. Отдельно подтвердите egress scope и технический control; он должен совпасть с карточкой, а не просто существовать в сети.',
+ 'Сверьте людей и дату. Requester access, authority role, record ids и expiry должны относиться к одной операции.',
+ 'Выпустите outcome. Либо передайте evidence в authorised workflow, либо зафиксируйте safe stop без payload и назначьте следующего владельца вопроса.',
+ ]),
+ h2('Ограничения и следующий проверяемый шаг'),
+ p('Этот field case — полностью synthetic. Его text не содержит настоящего production log, source code, тикета, PII, secret, customer identifier, file path или network address. Скрипт не обращается к filesystem, Git, CI, network, telemetry, clock, AI model или production. A positive decision не отправляет даже synthetic string: egressPerformed всегда false, а stop object имеет productionEffect not-attempted.'),
+ p('Следующий шаг: проведите tabletop review на пустой карточке одного реального use case. Пусть четыре владельца независимо заполнят class, policy/contract evidence, egress scope и authorization scope, не прикладывая payload. Затем внесите один deliberate gap: например, истёкший approval или неизвестный destination. Если процесс не может назвать reason, citation и next owner без копирования данных, сначала улучшите контур review, а уже потом подключайте инструмент к рабочему контексту.'),
+ h2('Историческая граница апреля 2025'),
+ p('Все внешние утверждения ограничены immutable GitHub Docs commit от 30 апреля 2025 и финальными NIST документами 2020/2023. Нет предположений о функциях, contract terms, data routing или гарантиях, появившихся позднее. Перед фактическим use case необходимо повторить проверку обычным HTTPS GET для своих документов и зафиксировать актуальные scope, date и exceptions.'),
+]);
+
+export const revisions = [practice, mechanism, field].map(({ proseLength, ...item }) => Object.freeze(item));
+
+function verifyFixture() {
+ const report = runAiDataPrivacyFixture();
+ const failed = Object.entries(report.assertions)
+ .filter(([, value]) => value !== true)
+ .map(([key]) => key);
+
+ if (failed.length > 0) {
+ process.stderr.write('FAIL fixture: ' + failed.join(', ') + '\n');
+ process.exitCode = 1;
+ return;
+ }
+
+ const count = Object.keys(report.assertions).length;
+ process.stdout.write('PASS fixture: ' + count + '/' + count + ' assertions\n');
+}
+
+if (process.argv.includes('--verify-fixture')) verifyFixture();
+if (process.argv.includes('--print-revisions')) process.stdout.write(JSON.stringify(revisions) + '\n');