Исторический материал. Измерения и статус относятся к дате исходного отчёта. Перевод сверён с английским оригиналом редакции 61e75e9 и не является новой редакцией отчёта.

Подтверждение реализации изменений с сохранением исходного оформления

Этот отчёт описывает этап реализации изменений на основе синтаксических деревьев без потери исходного оформления, завершённый коммитом bb9c169. Работа продолжала проектирование транзакционных изменений из задачи GitHub #1 (на английском). Отчёт дополняет нормативную матрицу приёмки этого этапа и не ослабляет её требований.

Команды проверки

Выполнять из корня репозитория после добавления всех файлов реализации:

go test ./bundle ./mutation
go test ./...
go test -race ./mutation -run '^(TestParserBackedMarkdownCorpus|TestParserBackedYAMLPresentationCorpus|TestPlan_InvalidYAMLReturnsPresentationErrorWithoutStage)$' -count=1
go test -race ./...
go vet ./...
go mod verify
go mod tidy -diff
go test ./mutation -run '^TestPlannerRepeatedPreviewAndValidationSemanticMatrix$' -count=1
go test -run '^$' -bench 'Benchmark(Overlay(Clone|Rename|Paths|ManifestDeltaRevision)|ParserBackedOverlayComparison|PlannerRepresentativeTransaction)$' -benchtime=1x -benchmem ./mutation
go test -run '^$' -fuzz=FuzzYAMLResolver -fuzztime=20s -parallel=1 ./mutation
go test -run '^$' -fuzz=FuzzRewriteMarkdownDestinations -fuzztime=20s -parallel=1 ./mutation
git diff --check

На заключительной проверке 2026-07-20 успешно прошли все приведённые выше команды: тесты, проверка гонок данных, vet, проверки модулей, матрица семантических сценариев, измерения производительности и проверка различий. После удаления всех пропусков сгенерированных входных данных и добавления независимой проверки атомарности общедоступного планировщика FuzzYAMLResolver прошёл три последовательных запуска по 20 секунд; отдельный запуск проверки Markdown без дополнительных ограничений также прошёл за 20 секунд.

Проверки со случайно сгенерированными входными данными намеренно запускаются отдельными командами: Go допускает только одну такую цель в каждом вызове go test. Детерминированная часть проверок теперь выполняется в процессе CI репозитория. Команды случайного тестирования и профилирования остаются воспроизводимыми ручными проверками: они не входят в проверки запросов на слияние, для которых важно время выполнения.

Зафиксированные входные данные используют собственный формат корпуса Go и автоматически загружаются соответствующими проверками:

Именованные входные данные регрессионных проверок сохраняют соответственно границу паники пустой встроенной ссылки, границу принадлежности непрозрачного потомка AST, границу вставки многострочного скаляра в двойных кавычках и порядок вставки смешанных скаляров и отображений.

Проверки также сохраняют явно заданные входные данные f.Add в mutation/markdown_presentation_test.go и mutation/yaml_resolver_test.go. Эти данные зафиксированы в исходном коде и доступны даже при удалении каталога testdata. Именованные файлы корпуса делают начальные входные данные доступными для ревью и загрузки механизмом случайного тестирования Go.

Поведенческие условия приёмки намеренно строже успешной компиляции:

Контрактные тесты bundle.SourceFromFS проверяют детерминированные обычные пути с прямыми косыми чертами и требование стабильного снимка без владения источником. internal/documentlayout.Split задаёт общую точную внешнюю границу блока метаданных для загрузчика и обоих анализаторов изменений. Матрица LF/CRLF/неканонических/незакрытых блоков не позволяет переотнести YAML-байты, принадлежащие загрузчику, к Markdown. Goldmark v1.8.2 и yaml.v3 v3.0.1 зафиксированы в go.mod; goccy и tree-sitter отсутствуют.

Примечание об измерениях производительности

Матрица наложения охватывает 0, 1, 100 и 10,000 видимых файлов для операций, определённых на пустом наложении: клонирования, Paths и разницы манифестов. Переименование в базовом и подготовленном состоянии проверяется на 1, 100 и 10,000 файлах. Для переименования публикуются обычные измерения времени и выделений памяти; отсутствие копирования содержимого не заявляется, когда измеряется защитное копирование при общедоступном чтении. Транзакция планировщика и матрица повторной семантической предварительной проверки/валидации охватывают 100, 1,000 и 10,000 понятий; для 10,000 использовать -benchtime=1x. Каждый замер сообщает выделения памяти, выносит подготовку примеров за измеряемый участок и проверяет семантику полученного результата.

Сохранённое сравнение исходного и итогового наложения использует один автономный пример с 10,000 файлами на коммите 43f7214 и итоговом дереве. Версия Go, оборудование, -benchmem, -benchtime=3x и три измерения одинаковы. Отдельный профиль планировщика содержит полную итоговую транзакцию с 10,000 понятиями и профили процессорного времени и выделений памяти.

Семантический сценарий планировщика с 10,000 понятиями содержит 10,001 видимый файл, включая index.md; files/op показывает именно этот размер. Это измерение производительности и профилирование без проверки гонок данных; сценарий по-прежнему выполняется в обычных тестах и однократном замере. Условие компиляции race пропускает только этот подтест при -race. Проверка гонок сохраняет повторную матрицу планировщика на 100/1,000 понятиях и все проверки корректности синтаксических анализаторов и наложения. Так проверка гонок не превращается в проверку тайм-аута исполнителя; догадки об окружении во время выполнения не используются.

Условия приёмки производительности основаны на поведении, а не на нестабильных межмашинных порогах ns/op:

TestOverlayPerformanceGates_NoPayloadCopiesOrUnchangedHashes подтверждает идентичность массивов после клонирования и переименования подготовленного файла и проверяет, что внедрённый алгоритм хеширования после Put не получает новых вызовов. TestParserBackedAllocationBaseline получает исходный уровень выделений памяти через testing.AllocsPerRun(100): для 64 Б и 1 МиБ значения должны точно совпадать, без допуска на размер содержимого, и не превышать восемь выделений. Зафиксированный исходный уровень клонирования — пять. Запас в три выделения на метаданные устраняет хрупкую зависимость от среды выполнения и размещения отображений, сохраняя запрет на копирование содержимого. При объединении разницы манифестов каждая квалифицированная контрольная сумма проверяется относительно сохранённого hash.Hash.Size(). Короткие, длинные и неканонические суммы, а также несовпадение Size/Sum приводят к отказу; Valid и объединение наложений не создают хеши для неизменившегося содержимого.

TestParserBackedMarkdownCorpus — детерминированная таблица Markdown: точные исходные диапазоны, отсортированное непересекающееся объединение, повторный разбор и неизменность байтов вне замен. Она включает многострочные адреса определений ссылок, проверяемые TestRewriteMarkdownDestinations_MultilineReferenceDefinition и TestMoveConcept_RewritesMultilineReferenceDefinition. MoveConcept передаёт классификацию и разрешение внутренних ссылок тому же контракту bundle.Link, что используется при построении графа. Он учитывает абсолютные пути набора, суффиксы запроса/фрагмента, идентификаторы понятий с двоеточиями и лексический переход к родительским каталогам.

TestRewriteMarkdownDestinations_NestedImageAndLinkOwnDistinctSpans доказывает структурную принадлежность равных адресов в [![image](a.md)](a.md): сопоставление скобок начинается в каждом узле Goldmark, пропускает диапазоны адресов потомков, принадлежащие анализатору, и должно находить ровно одного кандидата. Исключения с выбором первого совпадения для вложенных элементов нет. Если принадлежность разделителей не сокращает несколько кандидатов до одного, сборщик возвращает node_source_anchor_ambiguous.

TestPlanMoveConcept_PreservesOutgoingTargetsOfMovedDocument также подтверждает, что каждая статически разрешимая относительная ссылка, изображение и определение ссылки в перемещаемом документе разрешается относительно старого пути и записывается относительно нового. Несамоссылочные цели и суффиксы запроса/фрагмента сохраняют смысл при смене каталога. TestPlanMoveConcept_ReportsInvalidUTF8MarkdownBodyLocation сообщает о недопустимом UTF-8 в теле Markdown как об ошибке Markdown invalid_encoding в точной байтовой позиции полного файла, без подготовки изменений.

TestParserBackedYAMLPresentationCorpus — детерминированная таблица анализатора, разрешения и планировщика YAML. Она проверяет сохранение оформления, структурные ключи в кавычках, пунктуацию блочного контекста и границы многострочных простых скаляров, происхождение явных неспецифических тегов, принадлежность вставки между соседними узлами AST с посторонними непрозрачными расширениями, вставку/идемпотентность, маркеры потока с разделительными пробелами/комментариями, неоднозначность повторяющихся целей и канонических конечных точек, типизированные отказы Unsupported и Ambiguous со стабильными кодами, отсутствие подготовленных изменений и сохранение входных байтов. Нарушения проверки и инвариантов, например semantic_edit_mismatch и outside_span_changed, имеют тип Unsupported, а не Ambiguous. Ambiguous предназначен для нескольких семантических кандидатов (duplicate_relations / _relation_type / _target / _canonical_fragment), происхождения через псевдоним, повторяющихся нормализованных определений ссылок Markdown и нескольких исходных диапазонов адреса Markdown для одного узла AST. Это прямые тесты AAA, а не только подтверждение случайным тестированием.

TestEnsureRelationRejectsUnsupportedCanonicalSelectorProvenance применяет ту же проверку затронутого узла к исходным и целевым селекторам id/anchor: прямые якоря и явные теги отклоняются до подготовки изменений. TestPlannerRejectsCanonicalIdentityThroughDirectAlias аналогично проверяет псевдонимы отображений и последовательностей, чьи значения задают канонический идентификатор при переименовании и в обоих маршрутах конечных точек EnsureRelation. Это ошибки Ambiguous alias_provenance, а не общий отказ из-за неподдерживаемого затронутого якоря.

TestPlanPresentationErrorsCarryFullFileContext подтверждает, что PresentationError.Location остаётся внутри исходного файла. Диапазоны разрешения, заданные относительно YAML, включая происхождение исходного explicit_tag, сдвигаются; уже абсолютные диапазоны, например unterminated_frontmatter, остаются без сдвига. Проверки отказов при случайном тестировании Markdown и YAML дополнительно требуют, чтобы каждая типизированная ошибка оформления на непустом входе содержала непустой байтовый диапазон в пределах файла. Диагностика анализатора, нарушения семантической проверки и оборачивание в планировщике не могут регрессировать к пустому маркеру местоположения.

TestPlannerMapsGeneratedYAMLSyntaxFailureToOriginalInsertionRange и зафиксированная случайная регрессия подтверждают, что ошибки повторного разбора увеличенного буфера после вставки сопоставляются с исходным диапазоном замены, а не выдают смещения сгенерированного буфера за пределами исходного файла. TestPlannerYAMLAmbiguityLocationsCoverConcreteCandidates также подтверждает точные объединения диапазонов полного файла для повторяющихся связей, типов связей, целей и канонических фрагментов.

TestPlannerRejectsMergeDerivedTouchedKeysWithoutStage и TestPlannerRejectsMergeDerivedUpdateLookupsWithoutStage охватывают общедоступные маршруты добавления связи, перемещения и переименования. Затронутые ключи, полученные через слияние, возвращают alias_provenance на конкретном диапазоне <</псевдонима без подготовленного результата.

TestUpdateRoutesRejectDuplicateSemanticCandidatesWithoutStage подтверждает, что MoveConcept и RenameFragment отклоняют повторяющиеся экземпляры одной цели связи, повторяющиеся исходные канонические фрагменты и неоднозначные целевые канонические фрагменты до подготовки изменений. Различные ссылки, например a и a#fragment, остаются независимыми семантическими целями. Поиск фрагмента обходит всё разобранное дерево и не может определять принадлежность по первому совпадению. Проверка YAML со случайными данными также отклоняет любое успешное обновление повторяющихся экземпляров одной точной цели.

TestUnrelatedMergeProvenanceDoesNotBlockMutationRoutes задаёт дополняющую границу: слияние или псевдоним считается непрозрачным, если не может предоставить цель связи или канонический фрагмент, затронутый операцией. Перемещение, переименование и разрешение селекторов тогда могут продолжаться с побайтовым сохранением постороннего поддерева. Только пересекающееся происхождение через слияние приводит к отказу.

TestParseDocument_PreservesCRLFBody, TestParseDocumentTrimsExactlyOneBodyTerminator и TestParseDocument_PreservesBlockScalarTrailingNewlineBeforeClosingDelimiter подтверждают, что bundle.ParseDocument сохраняет точные байты блока метаданных и содержимое тела с CRLF и удаляет ровно один завершающий LF/CRLF сериализации. Предшествующий байт CR не повреждается, дополнительный перевод строки тела не удаляется.

Запускать измерения с -benchmem и сохранять при ревью версию Go, процессор и полный вывод. Результат зависит от оборудования и не задаёт жёсткого порога в наносекундах. Он используется для обнаружения существенных регрессий и обоснования возможного будущего отдельного предложения о HAMT или цепочке родительских состояний. Ни один из этих вариантов не относится к этапу изменений на основе синтаксических деревьев.

Сохранённый репрезентативный замер и полные текстовые профили процессорного времени и выделений памяти с 40 узлами находятся в отчёте планировщика. Двоичные профили в /private/tmp — воспроизводимые временные входные данные для отчёта, а не поставляемый результат.

Зафиксированное измерение наложения

Команда (2026-07-20, локальная рабочая копия):

go test -run '^$' -bench 'Benchmark(Overlay(Clone|Rename|Paths|ManifestDeltaRevision)|ParserBackedOverlayComparison|PlannerRepresentativeTransaction)$' -benchtime=1x -benchmem ./mutation

go version go1.26.5 darwin/arm64; система darwin/arm64, Apple M1 Max, macOS 27.0. Двоичные профили остаются воспроизводимыми временными файлами в /private/tmp; ссылка на сохранённый текстовый профиль приведена выше. Выбранные результаты:

BenchmarkOverlayClone/0-10                                  1        667 ns/op       240 B/op         4 allocs/op
BenchmarkOverlayClone/10000-10                              1     358000 ns/op   1049456 B/op        37 allocs/op
BenchmarkOverlayRename/base-10000-10                        1    1020708 ns/op    328752 B/op        12 allocs/op
BenchmarkOverlayRename/staged-10000-10                      1    1054833 ns/op    328736 B/op        11 allocs/op
BenchmarkOverlayPaths/base-0-10                             1       1500 ns/op       112 B/op         1 allocs/op
BenchmarkOverlayPaths/delta-0-10                            1       2375 ns/op       128 B/op         2 allocs/op
BenchmarkOverlayPaths/base-10000-10                         1    2232084 ns/op    928496 B/op        37 allocs/op
BenchmarkOverlayPaths/delta-10000-10                        1    2244458 ns/op    928496 B/op        37 allocs/op
BenchmarkOverlayManifestDeltaRevision/0-10                  1      20834 ns/op      1280 B/op        20 allocs/op
BenchmarkOverlayManifestDeltaRevision/10000-10              1    7695792 ns/op   3081624 B/op     80082 allocs/op

Полный профиль процессорного времени и выделений памяти на момент записи находится в указанном выше отчёте планировщика. Отсутствие копирования содержимого и хеширования неизменившихся данных подтверждается поведенческими тестами, а не выводится из короткого измерения с шумом.

Это измерение дерева на момент записи, а не целевой показатель или межмашинное сравнение. Контролируемое сравнение исходного и итогового состояния находится в отдельном сохранённом отчёте по ссылке.

Для обновлённой текстовой сводки профиля выполнить:

go test -run '^$' -bench 'BenchmarkOverlay(Clone|Rename|Paths|ManifestDeltaRevision)$' -benchtime=1x -benchmem -cpuprofile /private/tmp/okf-parser-backed-cpu.pprof -memprofile /private/tmp/okf-parser-backed-heap.pprof ./mutation
go tool pprof -top /private/tmp/okf-parser-backed-cpu.pprof
go tool pprof -alloc_space -top /private/tmp/okf-parser-backed-heap.pprof

Команды сохраняют двоичные профили в /private/tmp; сохраняемая текстовая проекция результата находится в указанном выше отчёте с 10,000 понятиями.