From 66c7364c37361ae117ecea0337474a5c74396b35 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 10 Aug 2026 20:07:02 +0300 Subject: [PATCH] =?UTF-8?q?fix(mxl-compile):=20=D0=B2=D1=8B=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=D1=8C=20=D0=B8=D0=B4=D0=B5=D0=BD=D1=82=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D1=80=20=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D0=B8=20=D0=BA=D0=B0=D0=BA=20?= =?UTF-8?q?=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?UUIDv3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Идентификатор колоночной раскладки выводится из имени детерминированно — это требование, а не оптимизация: случайный давал бы другой файл при каждой компиляции, снэпшоты не совпадали бы сами с собой, а повторная сборка того же определения порождала бы диф. Но выводился он неправильно: сырой хэш форматировался как UUID, без битов версии и варианта. В выдаваемом значении версия оказывалась 7, вариант 1 — таких не бывает, то есть формально это был не UUID, а шестнадцатеричная строка нужной формы. Платформа такое принимает (проверено сертификацией), но упереться в валидатор мы могли в любой момент — ровно того же сорта риск, что сертификация уже ловила в этой ветке. Теперь это штатный UUID версии 3 (имя + MD5, RFC 4122): для задачи «вывести идентификатор из имени» существует именно он. Детерминированность сохранена, оба порта дают одинаковое значение. Проверено: тесты 53/53 на обоих рантаймах, verify-snapshots 23/23. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/mxl-compile/scripts/mxl-compile.ps1 | 9 +++++++-- .claude/skills/mxl-compile/scripts/mxl-compile.py | 9 +++++++-- tests/skills/cases/mxl-compile/column-sets.json | 6 +++--- .../cases/mxl-compile/snapshots/column-sets/Template.xml | 4 ++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 5bfa3ea4..9670f66b 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 @@ -1,4 +1,4 @@ -# mxl-compile v1.21 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи) +# mxl-compile v1.22 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -400,8 +400,13 @@ function ConvertTo-LayoutId { if ($name -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') { return $name } + # UUID версии 3 (имя + MD5, RFC 4122): биты версии и варианта проставляются, иначе это + # не UUID, а просто шестнадцатеричная строка нужной формы. $md5 = [System.Security.Cryptography.MD5]::Create() - $h = ($md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($name)) | ForEach-Object { $_.ToString('x2') }) -join '' + $b = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($name)) + $b[6] = [byte](($b[6] -band 0x0F) -bor 0x30) # версия 3 + $b[8] = [byte](($b[8] -band 0x3F) -bor 0x80) # вариант RFC 4122 + $h = ($b | ForEach-Object { $_.ToString('x2') }) -join '' return "$($h.Substring(0,8))-$($h.Substring(8,4))-$($h.Substring(12,4))-$($h.Substring(16,4))-$($h.Substring(20,12))" } diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index b88aa428..3ddd2eb2 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.py +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-compile v1.21 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи) +# mxl-compile v1.22 — Compile 1C spreadsheet from JSON (+write_xml_file/write_utf8_bom: общий эталон записи) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import hashlib @@ -504,7 +504,12 @@ def main(): if re.match(r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' r'[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$', name): return name - h = hashlib.md5(name.encode('utf-8')).hexdigest() + # UUID версии 3 (имя + MD5, RFC 4122): биты версии и варианта проставляются, иначе это + # не UUID, а просто шестнадцатеричная строка нужной формы. + b = bytearray(hashlib.md5(name.encode('utf-8')).digest()) + b[6] = (b[6] & 0x0F) | 0x30 # версия 3 + b[8] = (b[8] & 0x3F) | 0x80 # вариант RFC 4122 + h = b.hex() return f'{h[0:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:32]}' column_layouts = [{'Id': None, 'Name': None, 'Size': total_columns, 'Widths': col_width_map}] diff --git a/tests/skills/cases/mxl-compile/column-sets.json b/tests/skills/cases/mxl-compile/column-sets.json index 2f72442e..57e8850b 100644 --- a/tests/skills/cases/mxl-compile/column-sets.json +++ b/tests/skills/cases/mxl-compile/column-sets.json @@ -1,5 +1,5 @@ { - "name": "Колоночные раскладки: своя ширина колонок у группы строк (id выводится из имени детерминированно, ожидаемый GUID = md5 от \"узкая\")", + "name": "Колоночные раскладки: своя ширина колонок у группы строк (id выводится из имени детерминированно, ожидаемый = UUIDv3 от \"узкая\")", "input": { "columns": 3, "columnWidths": { "1-3": 20 }, @@ -20,8 +20,8 @@ "fileContains": { "file": "Template.xml", "text": [ - "12320174-2b07-7806-1f7f-ec3834007fdb", - "12320174-2b07-7806-1f7f-ec3834007fdb" + "12320174-2b07-3806-9f7f-ec3834007fdb", + "12320174-2b07-3806-9f7f-ec3834007fdb" ] } } diff --git a/tests/skills/cases/mxl-compile/snapshots/column-sets/Template.xml b/tests/skills/cases/mxl-compile/snapshots/column-sets/Template.xml index 16f46637..6b068c79 100644 --- a/tests/skills/cases/mxl-compile/snapshots/column-sets/Template.xml +++ b/tests/skills/cases/mxl-compile/snapshots/column-sets/Template.xml @@ -31,7 +31,7 @@ - 12320174-2b07-7806-1f7f-ec3834007fdb + 12320174-2b07-3806-9f7f-ec3834007fdb 3 0 @@ -96,7 +96,7 @@ 1 - 12320174-2b07-7806-1f7f-ec3834007fdb + 12320174-2b07-3806-9f7f-ec3834007fdb 0