Это полная версия для чтения оригинала, зафиксированного на ревизии 61e75e9aa9a8719dfb480bf1f5226553b3a21d70. Перевод предназначен для чтения человеком и не изменяет нормативный оригинал или устанавливаемый пакет навыка. Для установки используйте руководство.

Переход с OKF 0.1 на 0.2

Эта инструкция описывает безопасную политику миграции библиотеки. Нормативные изменения перечислены в §13 зафиксированной спецификации OKF 0.2. Политика библиотеки не расширяет требования соответствия исходному стандарту.

Содержание

Что меняется

Два несовместимых изменения:

Остальные группы полей 0.2 необязательны. Миграция не обязана добавлять verified, status, stale_after, признаки достоверности источника или Attested Computation.

Контракт транзакции

  1. Постройте предварительный просмотр без записи.
  2. Зафиксируйте идентичность исходного состояния. Для v0.1-to-v0.2 также зафиксируйте детерминированную непустую контрольную сумму плана и доказательство версии 2 без содержимого документов, с обязательным resolution_digest.
  3. Получите явные входные данные только для смысловых преобразований, которые действительно будут выполнены.
  4. Сохраните неизвестные YAML, Markdown и ресурсы без потерь.
  5. Проверьте весь подготовленный набор как целевой формат 0.2.
  6. Выполните физическую запись или переименование корневого index.md последним. Изменение okf_version — последнее видимое действие публикации транзакции.
  7. Примените изменения одной транзакцией. Для применения патча используются expected_revision и контрольная сумма предварительного просмотра. Миграция v0.1-to-v0.2 использует доказательство версии 2, непустой expected_plan_digest и тот же полный expected_source. Операция target-noop для текущего состояния использует только expected_source, обходится без доказательства и проверяет целевой документ. Более ранние форматы доказательства не принимаются.

Частичная миграция запрещена. При любой неоднозначности набор остаётся без изменений; возвращается блокирующая причина или требуемое ручное действие.

Каждая ветвь без публикации — предварительный просмотр, отсутствие изменений, отклонение, блокировка, некорректный запрос или отмена — оставляет всё дерево файловой системы идентичным по путям и байтам и не создаёт .okf или промежуточных файлов. Изменения файловой системы может публиковать только авторизованная фактическая фиксация. Идентичное успешное повторное применение возвращает сохранённый результат без второй публикации.

Входные данные миграции проверяются до определения исходной версии. Каждое переданное поле автора, времени, цитаты, времени создания, вычисления или ресурса, нарушающее структуру либо правила предметной области, отклоняется даже для target-noop или набора без корневого документа. Гарантия отсутствия записи сохраняется.

Исходная версия определяется ровно один раз перед Preview. Preview и Apply используют один и тот же полный зафиксированный expected_source: requested_selector, состояние объявления (declaration_present, declaration_valid, declaration_raw, declared_version), поля разрешённой версии, происхождения и перехода, а также упорядоченные кандидаты и блокирующие причины. Apply отклоняет изменённое определение версии или его свидетельства до открытия хранилища.

Подстановка старых полей по §13 зависит только от наличия замещающего поля и не зависит от способа определения версии. Для версии по умолчанию, объявленной или явно указанной 0.1/0.2 и будущей версии действует одно условие. GeneratedPresent и SourcesPresent подавляют подстановку даже при некорректных значениях. TimestampAllowed и CitationsAllowed означают отсутствие замены; TimestampActive и CitationsActive дополнительно требуют наличия самой старой формы. CitationsActive требует точного заголовка # Citations, распознанного парсером; одного числового маркера недостаточно. Текст [1] без точного активного раздела не свидетельствует о старом формате. Набор без объявления версии только с таким маркером определяется как собственный случай target-noop формата 0.2, а не как предполагаемый переход с 0.1.

Общая предварительная проверка входных данных с учётом документов выполняется до планирования перехода, включая target-noop. Успешный target-noop текущего состояния в MCP не использует доказательство, не открывает хранилище, ничего не записывает и не создаёт .okf. CLI при предварительном просмотре строит доказательство без открытия хранилища; CLI с --write выполняет пустой CAS и сохраняет либо повторно использует долговечную квитанцию в .okf, не меняя файлы, учитываемые в ревизии набора.

Для v0.1-to-v0.2 сопоставление цитаты только по номеру выбирает нумерованную запись внутри активного старого раздела. Для target-noop это проверка повторного применения к уже мигрированному состоянию: соответствующая именованная сноска и, для заметки, структурированный ID источника и переданные метаданные должны существовать. Такое сопоставление не переписывает обычный текст [1]. Отсутствующее или отличающееся мигрированное содержание блокируется с migration_replay_mismatch без выдуманного ручного действия. Коллизия нормализованной метки с существующей сноской блокируется с normalized_footnote_label_collision и disambiguate_citation_entry. Активный старый раздел в целевом документе 0.2 тоже считается несовпадением повторного применения. Все эти ветви обходятся без доказательства и ничего не записывают.

Для каждого сопоставления с ненулевым legacy_number выбранный маркер [n], принадлежащий парсеру, должен исчезнуть, а ссылка на нормализованную именованную сноску [^SourceID] — присутствовать. Сопоставление только по тексту не требует ссылки из утверждения. Оставшаяся выбранная, отсутствующая или неверная ссылка блокируется с migration_replay_mismatch на точном участке, распознанном парсером, без записи или авторизации через доказательство и план. Похожие на маркеры байты внутри строчного кода, блоков кода и исходного HTML непрозрачны: они не служат свидетельством и не создают несовпадения повторного применения.

Отдельное поле миграции, которое нельзя представить в документе, возвращает invalid_request. Коллизия нормализованных SourceID внутри документа вместо этого блокируется с normalized_footnote_label_collision и disambiguate_citation_entry, включая target-noop. Коллизия в существующем документе сообщает тот же точный участок. Обе ветви ничего не публикуют и не записывают.

Apply перехода v0.1-to-v0.2, связанного с доказательством, может вернуть отсутствие изменений перехода только после проверки подлинности и повторного построения точного доказательства и контрольной суммы плана. Результат возвращается до открытия хранилища, .okf не создаётся. Для текущего target-noop MCP не использует доказательство и не открывает хранилище; CLI при предварительном просмотре строит доказательство без открытия хранилища; CLI с --write выполняет пустой CAS с долговечной квитанцией в .okf. На всех интерфейсах пути и байты файлов, учитываемых в ревизии, остаются идентичными.

Доказательство миграции содержит только зафиксированные метаданные: format_version: 2, обязательный непустой resolution_digest, запрос, полное определение исходной версии, исходную и результирующую ревизии, пути чтения, пары пути записи и контрольной суммы, удаления, переименования, канонические затронутые и обратные ссылки, сводки изменённых файлов и ссылок. Массивы не равны null; байты файлов, метаданные документа и текст отсутствуют. Более ранние форматы доказательства не принимаются. Отдельного expected_revision для миграции нет: при наличии доказательства определяющим является proof.base_revision. Предварительный просмотр без изменений или с блокировкой не возвращает доказательство; заблокированный план нельзя применить.

timestamp → generated

timestamp содержит время, но не идентичность автора. Поэтому миграция требует явно указанный generated.by.

В CLI okf migrate --actor задаёт явного автора документа для generated.by. Это не store.ChangeSet.Actor: субъект транзакции адаптера CLI отделён от автора и определяется внутренне. Предварительный просмотр без записи может не содержать --actor и вернуть ручное действие; --write требует автора перед созданием generated. Для полей автора в MCP отдельное ограничение транспорта и ресурсов в 256 байт проверяется до общего ValidActor. При 257 и более байтах возвращается resource_limit, а не ошибка некорректного автора. Этот предел не является грамматикой автора и не ограничивает предметную область набора или хранилища.

Безопасный случай:

# до миграции
timestamp: 2026-05-28T22:53:05Z

# после миграции; участника задаёт вызывающая сторона
generated:
  by: "process:catalog-export"
  at: 2026-05-28T22:53:05Z

Правила:

# Citations → sources

Миграция не сопоставляет источники по смыслу. Вызывающая сторона явно задаёт соответствие каждой старой записи стабильному ID источника и структурированному ресурсу.

CLI принимает только JSON-файл ограниченного размера --citation-mappings <json-file> с точным закрытым массивом верхнего уровня: [{path,entries:[{legacy_number?,legacy_entry?,source_id,title?,resource?}]}]. Поле MCP содержит тот же массив. path — путь Markdown относительно набора, включая корневой index.md, журналы и вложенные файлы. Каждая запись требует одно или оба условия выбора: ненулевой legacy_number для [1] либо точный непробельный legacy_entry для ненумерованного пункта или исходного URL. Если переданы оба условия, оба должны совпасть с одной существующей записью.

legacy_entry должен быть корректным UTF-8 длиной 1–4096 байт, содержать непробельный текст после TrimSpace и не содержать NUL. TAB, LF и CR разрешены; остальные управляющие символы C0 и DEL запрещены. Точные байты не обрезаются и не нормализуются по регистру, URL или переводам строк. К ним привязаны авторизация и контрольная сумма, поэтому LF и CRLF различаются. Выбор только по тексту, совпадающий с несколькими одинаковыми исходными записями, неоднозначен и отклоняется. Повторение ненулевого legacy_number всегда запрещено. Один legacy_entry допустим только в различных полных парах номера и текста. Выбор только по тексту пересекается с любым повторным использованием того же исходного текста; два таких условия выбора тоже запрещены. Разные полные пары с разными номерами допустимы.

Канонический порядок: путь, legacy_number, точный legacy_entry. Пересекающиеся условия выбора, неизвестные поля и несовместимые метаданные повторно используемого ID источника запрещены.

# до миграции
Утверждение подтверждено документом.[1]

# Citations

[1] [Учебные правила](https://example.invalid/policy)
# после миграции
---
type: Reference
sources:
  - id: policy
    resource: https://example.invalid/policy
    title: Учебные правила
---

Утверждение подтверждено документом.[^policy]

[^policy]: Учебные правила.

Правила:

Если sources уже присутствует вместе со старым Citations, чтение использует sources: старые значения подставляются только при отсутствии замены. Миграция сохраняет обе исходные формы и всегда блокируется; объединение, удаление повторов и удаление данных не выполняются. Ручное действие — reconcile_sources_and_citations; вызывающая сторона должна заранее нормализовать документ.

Коды ручных действий:

Явный путь для цитаты, времени создания или вычисления должен указывать на существующий документ набора. Отсутствие документа блокирует операцию с migration_document_missing, возвращает пустой список ручных действий и не позволяет создать документ через сопоставления; дополнительный код действия не вводится.

Неоднозначный выбор по повторяющемуся исходному тексту не является неоднозначностью назначения ссылки.

Расширение представления RelationRef в skosovsky/okf

Эта грамматика — расширение библиотеки, а не правило миграции исходного стандарта OKF 0.2. Ссылка отношения имеет вид <escaped-concept-id>[#<fragment>]. Только # внутри ConceptID записывается как логическое \#; первый неэкранированный # отделяет фрагмент. Поэтому source#part и source\#part обозначают разные сущности: заметку с фрагментом и корневую заметку с ID source#part. Обратные косые черты после разделителя остаются буквальными байтами фрагмента. Постороннее или неканоническое экранирование ID отклоняется; обычные ссылки сохраняются побайтно.

Запись в одинарных кавычках YAML: 'source\#part'. В JSON та же логическая ссылка: "source\\#part". Схемы графа и MCP сохраняют строковую форму, а формат квитанции хранилища версии 1 продолжает хранить ссылки как []string.

Граница Attested Computation

Миграция не превращает описательный текст, SQL-фрагменты или примеры команд оболочки в Attested Computation автоматически. Это отдельное решение автора:

Запрещённые выводы

Не выводите из миграции, времени, истории Git или проверки структуры:

Чтение старого формата после появления средств миграции

До применения изменений и для намеренно сохранённых наборов старого формата потребитель 0.2:

Проверка перед применением