# VII. Руководство по полной реализации Этот том описывает инженерный путь к совместимому движку FParkan. Он опирается на доказанные форматы и runtime-контракты, но не требует повторять физическое деление оригинала на пятнадцать DLL. Повторить нужно наблюдаемое поведение: форматы, имена, fallback, object IDs, порядок событий, численную политику, границы кадра, сохранения и воспроизводимость прохождения. Предложенные ниже modules, handles, snapshots, queues и scheduler phases являются целевой архитектурой новой реализации, а не восстановленным внутренним layout оригинального Iron3D. Главная практическая цель: запускаться из неизменённого оригинального каталога игры, проходить corpus gates для демоверсии, Части 1 и Части 2, а затем измеримо двигаться от archive compatibility к полной игровой совместимости. ## Целевая архитектура Практичная форма новой реализации -- модульный монолит с узкими интерфейсами и отдельными platform adapters. Внутренние границы должны соответствовать ролям Iron3D, а не обязательно его DLL. Это упрощает перенос на современные платформы и оставляет возможность поддерживать разные compatibility profiles для разных сборок данных. ```text application запуск, окно, конфигурация, shutdown platform filesystem, clocks, input, threads, dynamic libraries resources NRes, RsLi, paths, archives, cache and diagnostics assets MSH, WEAR, MAT0, Texm, FXID and auxiliary formats mission TMA, unit DAT, prototype graph, scenario data world ObjectId, queue, lifecycle, time, messages, mirrors terrain Land.msh, Land.map, surface and spatial queries navigation areals, graph search, corridors behavior unit state machines, target and path requests physics control systems, collision proxies and contacts animation pose sampling, hierarchy and blending audio sample cache, sources, listener and buses render legacy-state compatibility and modern backend network game message schema plus transport adapters tools validators, extractors, viewers, captures and editors ``` Каждый модуль зависит от нижележащих интерфейсов, а не от concrete managers. Behavior видит `INavigation` и `IPhysicsCommandSink`, но не включает headers renderer-а. Render получает immutable snapshot, а не mutable world. Network receive не меняет мир напрямую: validated messages попадают в очередь следующей calculation boundary. ### Центральные идентичности Resource identity хранит и исходное написание, и нормализованный ASCII-key для поиска: ```c struct ResourceKey { NormalizedRelativePath archive; FixedAsciiName name; uint32_t type_id; }; ``` Normalization сохраняет исходную строку для diagnostics и roundtrip, а отдельный ASCII-casefold key используется только для lookup. Эта граница важна для архивов [NRes](../reference/nres.md), таблиц [RsLi](../reference/rsli.md), prototype references и fallback-путей материалов. Object identity разделяет внутреннюю защиту от dangling references и исходную сетевую/script-семантику: ```c struct ObjectHandle { uint32_t generation; uint32_t slot; }; struct OriginalObjectId { uint32_t raw; }; ``` `ObjectHandle` нужен для безопасного внутреннего владения, deferred deletion и weak references. `OriginalObjectId` сохраняет наблюдаемую семантику исходной игры: scripts, mirrors, network messages и savegame references должны видеть логический ID, а не адрес объекта или номер slot в новом allocator-е. Frame snapshot отделяет simulation от render. Simulation пишет mutable state; renderer читает опубликованное состояние или строго ограниченную фазу `in_render`. Deferred deletion применяется между фазами, а не во время traversal. Командный контур renderer-а должен сверяться с [описанием кадра](../reference/render-frame.md) до pixel comparison. ### Владение ресурсами Ресурс проходит несколько уровней: ```text ArchiveHandle -> EntryView -> DecodedBlob -> ParsedAsset -> RuntimeResource ``` `EntryView` ссылается на metadata архива, `DecodedBlob` владеет подготовленными bytes, `ParsedAsset` является CPU-представлением, `RuntimeResource` может дополнительно владеть GPU/audio objects. Eviction верхнего уровня не закрывает архив, если он ещё нужен другому entry. Ссылки идут вниз только через явные handles. Для shared objects допустимы reference counting или generation handles. Intrusive refcount нужен только в ABI-shim; внутренний современный код предпочтительно держит понятное владение и weak handles. Архивы, decoded blobs, CPU assets и GPU resources имеют отдельные бюджеты и отдельные diagnostics. ### Backend adapters Render, audio, input и network получают отдельные adapters. Legacy compatibility state живёт выше Vulkan, D3D11 или Metal backend; DirectPlay compatibility живёт отдельно от modern transport. Так можно заменить платформу, не меняя форматы, игровую семантику и regression corpus. Backend adapter не должен быть местом, где исправляются данные. Если [MSH](../reference/msh.md), [MAT0](../reference/materials.md) или [Texm](../reference/texm.md) требуют fallback, это фиксируется в asset/runtime слое и попадает в trace. Backend получает уже выбранные resources, states и draw items. ### Scheduler phases ```text collect_platform_events build_input_snapshot advance_game_clock calculate_world_queue apply_deferred_operations update_navigation_physics_animation_fx publish_render_snapshot render_world render_ui end_frame_callbacks maintenance_and_eviction ``` Фазы имеют стабильный порядок и запрещённые операции. Registry mutation запрещена во время world traversal, GPU upload не изменяет simulation state, а maintenance не влияет на gameplay. Script timers, material animation и FX lifetime относятся к game time, если обратное не доказано. Сначала реализуется однопоточный эталон. Параллелизм добавляется только внутри фаз с детерминированным merge: decoding независимых assets, culling chunks или подготовка immutable draw items. Это снижает риск скрытых race conditions и расхождений replay. ### Структурированные ошибки Каждая ошибка должна содержать фазу, путь, archive entry, object/prototype key, offset и цепочку причины. ```text MissionLoadError mission: Campaign.00/Mission.02 object: 17 resource_name: UNITS/.../unit.dat component: e_tur_... prototype: objects.rlb::e_tur_... cause: model archive missing ``` Логическое отсутствие необязательного lightmap, отсутствующий entry в архиве, неизвестное opaque поле, выход ссылки за диапазон и повреждённый offset имеют разный severity и разные способы исправления. Ошибка данных должна быть actionable chain, а не строка вида `failed to load resource`. ## Порядок работ Движок строится от данных к поведению и от детерминированных CPU-компонентов к аппаратным. Каждый этап заканчивается исполняемым инструментом и тестовым критерием. Нельзя начинать полноценный gameplay, пока ресурсный граф и model/material path не дают воспроизводимый результат. ### Этап 0. Corpus harness - индексировать оригинальный каталог и вычислить hashes; - реализовать bounded binary cursor и structured diagnostics; - создать CLI для массового запуска parser-ов; - сохранять JSON-отчёт с counts, variants, warnings и failures; - зафиксировать демоверсию, Часть 1 и Часть 2 как независимые baselines. Готовность: повторный запуск на каждом неизменённом каталоге даёт идентичный отчёт. Любой parser умеет завершиться контролируемой ошибкой с offset и контекстом, а не crash или allocation по непроверенному count. ### Этап 1. Архивы и пути - реализовать strict/lossless [NRes](../reference/nres.md) reader/writer; - реализовать [RsLi](../reference/rsli.md) mapping, table transform, lookup, LZSS и Deflate; - добавить адаптивный decoder для методов `0x080` и `0x0A0`; - воспроизвести overlay и известные compatibility quirks; - реализовать archive-handle cache и ASCII name policy. Готовность: неизменённые архивы проходят byte-identical roundtrip; поиск всех имён совпадает с каталогом; malformed corpus отклоняется без выхода за память. NRes с ненулевым unindexed region обязательно остаётся regression case. ### Этап 2. Граф ресурсов - разобрать `objects.rlb` и unit DAT; - построить resolver прямой MSH, рекурсивного parent prototype через `objects.rlb` и отдельного BASE payload; - реализовать dependency graph с reachability от миссии; - добавить parsers CTPT, NDPR и остальных служебных форматов в lossless-режиме; - создать инспектор прототипа, показывающий все связанные ресурсы. Готовность: 201 demo-объект раскрывается в 501 прототип. Затем все миссии Частей 1 и 2 дают 4 701 и 5 845 prototype requests без failures. Недостижимые отсутствующие ресурсы отмечаются отдельно от критических ошибок в reachable graph. ### Этап 3. Статический asset viewer - реализовать [MSH](../reference/msh.md) core streams, slots и batches; - декодировать Texm во все подтверждённые pixel formats; - разобрать WEAR и [MAT0](../reference/materials.md) с точными fallback; - построить современный renderer compatibility layer; - добавить wireframe, normals, bounds, LOD/group и material debug views. Готовность: открываются 435/511 моделей, 518/631 textures и 905/1 127 materials Частей 1/2; batch/index bounds не нарушаются; viewer показывает корректно текстурированную статическую модель из исходного архива. Красивый viewer всё ещё означает только asset compatibility, а не готовую игру. ### Этап 4. Анимация и эффекты - реализовать MSH type 8/type 19 sampling и hierarchy; - добавить x87-compatible reference path для чувствительных формул; - реализовать material phase animation; - разобрать FXID header/commands и runtime instances; - сначала поддержать все opcodes, встречающиеся в корпусе, сохраняя raw body; - добавить deterministic RNG stream и effect capture. Готовность: frame-by-frame poses совпадают с golden reference своей части; все 923/1 065 FXID создаются без parser errors; перезапуск одинакового effect seed даёт идентичный список emitted primitives. ### Этап 5. Карта и мир - реализовать `Land.msh` и corrected `TerrainFace28` layout; - построить terrain rendering и CPU surface queries; - реализовать `Land.map`, cell grid и graph links; - визуализировать areals и найденные маршруты; - разобрать [TMA](../reference/tma.md) и выполнять staged mission loading; - создать World3D queue, ObjectId и deferred deletion. Готовность: 65 карт и 60 TMA Частей 1 и 2 загружаются до EOF; все areal links валидны; objects появляются в правильных transforms; мир выдерживает расчётные шаги без рендера. ### Этап 6. Gameplay controllers - подключить input snapshot и camera controller; - реализовать navigation corridor, Behavior state machine и Wizard boundary; - создать physical controller и collision manager; - загрузить control resources в lossless typed model; - внедрить game time, pause, event queue и end-of-frame callbacks; - подключить AI layer и symbol/event layer сценариев. Готовность: юнит получает цель, строит маршрут, движется по terrain, реагирует на collision и исполняет базовые миссионные события в детерминированном replay. На этом этапе вводится differential branch для изменённых `AniMesh`, `Control` и `Effect`; неизменённые DLL используют общий reference path. ### Этап 7. Полный кадр, звук и UI - реализовать render phases, sorting, lighting, shadows и atmosphere; - подключить 3D listener, sample cache, FX sounds и mission audio; - воспроизвести shell/UI loading и post-world pass; - добавить frame capture до UI и после UI; - зафиксировать capability fallback profiles. Готовность: миссия визуально и звуково проходима; каждый draw и sound event имеет trace; одинаковый replay создаёт одинаковые command lists. На этом этапе вводится differential branch для `iron3d` и `services`. ### Этап 8. Сеть, сохранения и динамическая совместимость - реализовать modern transport над versioned game-message schema; - отдельно исследовать DirectPlay wire и `netZipData` для native compatibility; - добавить mirrors, ownership transfer и disconnect cleanup; - восстановить save/campaign state и dispatcher; - выполнить динамические captures оригинала для render states, script VM и physics edge cases. Готовность: одиночная кампания запускается из оригинального каталога, сохраняется и продолжается; multiplayer replay согласован между peers; full corpus не создаёт новых parser variants без явной регистрации. ## Тестовый контур Совместимость нельзя подтвердить одним screenshot. Нужны тесты на уровне bytes, структур, ссылок, simulation state, команд renderer-а и конечного изображения. Каждый слой локализует свой класс ошибки. ```text unit tests -> parser/property tests -> corpus validation -> cross-resource integration -> deterministic simulation replay -> render/audio command captures -> pixel and gameplay parity ``` Failure верхнего уровня всегда должен позволять спуститься к меньшему тесту и понять причину. ### Unit, property и fuzz tests Для каждого binary primitive проверяются little-endian чтение, bounded strings, checked arithmetic и cursor boundaries. Для структур -- минимальный размер, максимальные counts, пустые arrays, нулевые варианты и редкие branches. Property tests генерируют случайные корректные NRes/RsLi/WEAR records, выполняют encode -> decode и сравнивают семантику. Fuzz tests изменяют длины, offsets, counts и termination bytes и требуют контролируемой ошибки без crash и чрезмерного выделения памяти. Критические алгоритмы имеют отдельные vectors: ASCII casefold, NRes permutation search, RsLi byte transform, LZSS backreferences, quaternion shortest path, matrix composition и terrain mask remap. ### Corpus validation Каждый файл оригинального каталога проходит parser своего семейства. Отчёт содержит hash, variant, counts, warnings, errors и точный offset сбоя. Baseline демоверсии: ```text MSH 435 MAT0 905 Texm 518 FXID 923 WEAR 457 Land.msh 6 Land.map 6 TMA 6 unit DAT 425 errors 0 ``` Изменение parser-а принимается только если baseline остаётся стабильной либо новый variant зарегистрирован с образцом и объяснением. Warnings должны быть именованными: «неизвестное opaque поле» не равно «выход ссылки за диапазон». ### Cross-resource integration Интеграционный тест начинается с миссии и проходит весь dependency graph: object -> prototype -> MSH -> WEAR -> MAT0 -> Texm/lightmap/FXID. Он не ограничивается тем, что файлы существуют: material slot должен указывать на допустимый MAT0, phase -- на допустимую texture, model batch -- на существующий WEAR index. Demo mission total: 201 objects -> 501 prototypes -> 501 object MSH/WEAR. Чистый object graph даёт 3 873 material slots и 5 049 texture requests; после включения environment WEAR итог равен 3 879 material slots, 5 067 textures и 18 lightmaps, failures 0. Такой тест ловит ошибки casefold, suffix, fallback и путей, которые отдельный parser не замечает. Для каждого отсутствующего узла отчёт хранит полный parent chain, чтобы различать broken global archive и реально достижимый mission failure. ### Deterministic simulation replay Записывается начальная миссия, seed, input events, network messages и значения внешних часов. На контрольных ticks сохраняется canonical state hash: ```text sorted ObjectId list transforms and velocities critical properties and owners AI/behavior state IDs active effect state game clock and RNG states ``` Pointer addresses, allocator order и GPU handles в hash не входят. Два запуска с одинаковым log должны давать одинаковый state hash на каждом checkpoint. Первое расхождение гораздо информативнее финального разного результата миссии. ### Render command parity До pixel comparison сравнивается command list: ```text camera matrices and viewport visible ObjectIds render phase and stable order model/node/slot/batch IDs material phase and texture handles legacy pipeline states index ranges and transforms ``` Если command lists совпадают, но pixels различаются, проблема находится в shader/backend, sampling или численной точности. Если command lists уже различаются, pixel diff лишь скрывает более раннюю ошибку. Golden captures следует хранить отдельно для статической модели, анимации, terrain, transparent FX, shadows, lightmap и atmosphere. ### Pixel, audio и network tests Pixel tests используют фиксированное разрешение, camera, device profile, seed и timeline. Сравниваются exact pixels для CPU/reference path и tolerance metrics для GPU path, но tolerance не должна скрывать переставленные прозрачные primitives. Audio tests сравнивают список sound events, sample IDs, positions, loop flags и gains; waveform зависит от mixer/device и является вторичным уровнем. Network tests воспроизводят captured message sequences, проверяют mirrors, ownership и disconnect. Для native DirectPlay compatibility дополнительно нужен packet-level corpus. ## Regression baselines Corpus validation формирует три независимых отчёта: демоверсия, Часть 1 и Часть 2. Каждый сохраняет manifest файлов, hashes executable/DLL, variants, warnings, global archive health и mission reachability. Ключевые corpus gates: ```text NRes: 120 файлов / 6 804 entries и 134 / 8 171 для Частей 1/2 TMA: 29 миссий / 864 objects / 28 extras и 31 / 885 / 41 MSH: 435 и 511 моделей MAT0: 905 и 1 127 материалов Texm: 518 и 631 текстура FXID: 923 и 1 065 эффектов full reachability: 4 701 и 5 845 prototype requests, failures 0 ``` Расширенные mission-reachability totals: ```text Часть 1: 29 TMA, 864 objects, 4 701 prototypes, 36 954 materials, 48 806 textures, 139 lightmaps, failures 0 Часть 2: 31 TMA, 885 objects, 5 845 prototypes, 50 888 materials, 68 603 textures, 214 lightmaps, failures 0 ``` Обязательные regression cases: - NRes с ненулевым unindexed region; - prototype inheritance через `objects.rlb`; - unit DAT `description[32]` без NUL; - TMA epilogue и `extra_count` 0--4; - empty SWAV entry; - stale save-slot metadata без payload; - build-scoped RVA lookup. Byte-identical asset comparison выполняется только внутри одного корпуса. Между Частями 1 и 2 сравниваются semantic invariants и decoded representation, поскольку многие assets пересобраны. ## Точность, скорость и повторяемость Совместимый движок должен быть корректным, повторяемым и достаточно быстрым. Эти свойства нельзя получать одним и тем же приёмом. Сначала создаётся простой эталонный путь, затем он измеряется и оптимизируется без изменения результата. Главные источники расхождений: x87 extended precision, преобразование float в integer, порядок операций, старые SIMD implementations, нестабильная сортировка, RNG и использование разных часов. ### x87 и округление Оригинальный x86-код мог хранить промежуточные значения в 80-битных регистрах x87, а в память записывать 32-битный float. Современный compiler чаще использует SSE с округлением после каждой операции. Различие заметно на границах animation frame, culling plane и collision threshold. Для критических формул нужен reference mode: - фиксированный порядок операций без reassociation; - запрещённый fast-math; - явные преобразования и проверенный режим округления; - тесты возле half-integer и epsilon boundaries; - при необходимости extended intermediate через `long double` на проверенной платформе. Не требуется эмулировать x87 во всём движке. Нужно локализовать функции, где малое отличие меняет дискретное решение, и держать для них scalar reference path. ### RNG как часть состояния FX, atmosphere и, вероятно, AI используют случайные значения. Один глобальный RNG легко расходится, если новая реализация запрашивает дополнительное число для визуальной оптимизации. Для трассировки полезны именованные streams: ```text world/gameplay RNG AI/script RNG FX instance RNG atmosphere RNG non-deterministic cosmetic RNG ``` Для native parity может потребоваться один общий алгоритм и точная sequence. До подтверждения capture каждый stream хранит seed и счётчик вызовов в trace. Cosmetic stream не входит в simulation hash. ### Стабильный порядок Коллекции не должны зависеть от адресов, unordered containers или порядка завершения worker threads. Для объектов, collision pairs, opaque/transparent draws и network messages задаются явные stable keys: - objects -- queue insertion sequence или OriginalObjectId; - collision pairs -- упорядоченная пара IDs; - opaque draws -- phase, pipeline key, material, stable insertion ID; - transparent draws -- layer, quantized distance, stable insertion ID; - network messages -- sequence и sender. Даже когда математический результат коммутативен, side effects, cache accesses и RNG делают порядок наблюдаемым. ### Часы и fixed-step Monotonic platform clock хранится отдельно от game clock. Pause и time scaling применяются к game clock. Simulation работает с фиксированным или точно воспроизводимым шагом, а render может интерполировать presentation state, не изменяя authoritative world. Maintenance timers кэшей используют реальные часы или отдельную подтверждённую шкалу; их срабатывание не должно менять gameplay. При перегрузке лучше выполнить ограниченное число simulation steps и явно зафиксировать dropped presentation frames, чем передать огромный `dt` в AI/physics. ### Оптимизация без потери эталона 1. Сохранить scalar reference implementation. 2. Добавить profiler counters на decoding, culling, sorting, animation, upload и draw. 3. Оптимизировать только измеренный bottleneck. 4. Сравнить SIMD/parallel результат с reference на полном corpus. 5. Оставить runtime switch для отключения оптимизации при диагностике. `g_FastProc` удобно моделировать как таблицу function objects: все slots сначала указывают на scalar path, затем безопасные slots заменяются SIMD-вариантами после self-test на старте. ### Кэш и память Архивы, decoded blobs, CPU assets и GPU resources имеют отдельные budgets. Eviction разрешена только для объектов с нулевым external refcount и после безопасной frame fence. Original delayed cleanup порядка десятков секунд можно воспроизвести policy-параметрами, не сканируя все entries каждый кадр. Основные показатели: число открытых архивов, decoded bytes, resident textures/lightmaps, models, active FX, draw items и deferred-delete size. Любой неограниченно растущий счётчик является regression. Производительность считается достаточной только после корректности: стабильные 60 FPS с неверным LOD или пропущенными эффектами не являются успехом. ## Release gates Версия не выпускается, если: - появился новый corpus error; - изменился byte roundtrip неизменённых ресурсов; - dependency graph получил failure в достижимом пути; - deterministic replay расходится; - command capture изменился без ожидаемого changelog; - parser допускает allocation по непроверенному count; - новая оптимизация не имеет scalar reference comparison. Каждое исправление регистрирует минимальный regression asset или synthetic vector. Если новый behavior намеренно отличается от предыдущего, изменение должно иметь compatibility profile, corpus sample и объяснение, почему старый baseline был неполным или неверным. ## Уровни совместимости Слово «совместимый» используется только с уровнем: 1. **Archive-compatible** -- открывает и сохраняет контейнеры. 2. **Asset-compatible** -- декодирует модели, материалы, текстуры и эффекты. 3. **Mission-compatible** -- загружает карту и создаёт все объекты. 4. **Runtime-compatible** -- исполняет время, события, поведение и физику. 5. **Presentation-compatible** -- воспроизводит рендер и звук. 6. **Game-compatible** -- позволяет пройти миссии, сохраняться и продолжать. 7. **Native-interoperable** -- взаимодействует с оригинальной сетью и внешним ABI. Viewer с красивой моделью находится только на втором уровне. ### Обязательные критерии запуска и данных - приложение запускается из неизменённого оригинального каталога; - относительные пути, регистр и legacy encodings разрешаются по исходным правилам; - все требуемые NRes/RsLi открываются без предварительной конвертации; - parsers проверяют границы и не используют неопределённые bytes как указатели; - неизвестные поля сохраняются lossless; - все mission-reachable prototype, model, material, texture, lightmap и effect references разрешаются; - отсутствие необязательного ресурса следует документированному fallback, а не случайному default. ### Обязательные критерии мира - TMA разбирается до точного EOF; - `Land.msh` и `Land.map` создают корректную поверхность и areal graph; - ObjectId, owner и mirror semantics устойчивы; - queue traversal и deferred deletion безопасны; - pause, game time и simulation steps повторяемы; - AI/Behavior/Wizard/Control взаимодействуют через заданные границы; - collision и navigation не подменяют друг друга; - script events используют logical IDs и переживают удаление объектов; - deterministic replay совпадает на контрольных ticks. ### Обязательные критерии presentation - static и animated MSH используют правильные slots, batches и transforms; - WEAR/MAT0/Texm fallback и phase timing совпадают; - mip-skip, palettes, Page atlases и lightmaps работают; - render phases, depth/cull/blend state и transparent order подтверждены captures; - FXID commands и RNG дают устойчивый результат; - camera и 3D sound listener синхронизированы; - atmosphere, тени, солнце и flares не являются декоративными заглушками; - UI и world rendering имеют правильную границу; - golden command captures стабильны, pixel parity измеряется на фиксированных сценах. ### Обязательные критерии полной игры - все доступные миссии стартуют, завершаются и корректно сообщают success/failure; - campaign dispatcher сохраняет прогресс; - savegame восстанавливает world, script, AI, RNG и clocks, а не только placement; - input remapping, pause, camera modes, sound и настройки работают из UI; - длительный прогон не накапливает objects, resources или audio sources; - ошибки данных показывают actionable chain; - производительность приемлема без отключения подсистем; - демоверсия, Часть 1 и Часть 2 проходят один и тот же тестовый контур с раздельными manifests и эталонами. ### Native interoperability Самый строгий уровень дополнительно требует совпадения x86 ABI экспортов, vtable slots и calling conventions для подключаемых оригинальных модулей, а также DirectPlay wire/framing и compression. Этот уровень независим от возможности играть в новом standalone runtime. Проект может честно заявлять game compatibility без native DLL/network interoperability, но это должно быть явно указано. Аналогично pixel-perfect режим может быть отдельным compatibility profile поверх функционально корректного renderer-а. ### Совместимость нескольких наборов данных Критерий полной совместимости применяется отдельно к демоверсии, Части 1 и Части 2. Прохождение одного набора не позволяет заявлять поддержку остальных. Обязательное различие: - **format compatibility** -- один parser принимает все три набора; - **content compatibility** -- конкретная миссия разрешает весь reachable graph; - **behavior compatibility** -- runtime совпадает с соответствующей сборкой изменённых DLL; - **cross-version support** -- один новый движок выбирает корректные данные и defaults по fingerprint установки. Content fingerprint включает hashes executable/DLL и manifest ключевых архивов. Он не используется для запрета модификаций, но выбирает compatibility profile и делает отклонение диагностируемым. ## Definition of done Полное документирование и реализация считаются завершёнными только когда каждый критерий связан с главой спецификации, executable test и хотя бы одним corpus/golden case. Утверждение без проверяемого критерия остаётся исследовательской заметкой, а не контрактом.