Пользовательские вычисляемые поля — отдельный блок <dcsset:userFields>
в начале settings. Два подтипа:
- UserFieldExpression: dataPath + title + detail{expression,presentation}
+ total{expression,presentation}
- UserFieldCase: dataPath + title + cases[{filter, value, presentation}]
DSL: тип определяется наличием 'cases' (case-форма) vs detail/total
(expression-форма) — без явного 'type'.
В SKILL.md не упоминаем (редкая фича — обычно настраивается пользователем
через UI «Изменить вариант»). Описано в spec.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Build-TableAxisBlock теперь читает filter и outputParameters блока,
order/selection сохраняются с точным присутствием (даже [Auto]) для
bit-perfect round-trip.
Дополнительно: nestedSchema (вложенные DCS-подсхемы) добавлены в Ring 3
fail-fast — фича редкая (15/490 в ERP), требует значительного
расширения DSL (рекурсивная DCS внутри JSON). Поддержку можно
вернуть позднее как nestedSchemas массив в корне JSON.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Колонки и строки таблицы (StructureItemTable.column/row) и оси диаграммы
(StructureItemChart.point/series) могут иметь свои filter, order,
selection, outputParameters — реальные отчёты активно это используют
для отбора и оформления внутри каждой оси.
Compile теперь:
- эмитит filter и outputParameters на column/row/point/series
- order/selection эмитятся только если заданы в JSON (раньше дефолтили
[Auto], что иногда расходилось с оригиналом)
Логика вынесена в общий helper Emit-TableAxisBlock.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Build-DataSet для типа DataSetUnion теперь читает <item xsi:type="...">
(платформенный формат), сохранена обратная совместимость с <dataSet>
для XML, сгенерированных предыдущими версиями skd-compile.
Эффект на sample30: -12000 строк diff (LOST <v8:item>/<lang>/<content>
в полях inner-Union datasets).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Платформенный 1С пишет вложенные dataSets внутри DataSetUnion как
<item xsi:type="DataSetQuery">, а наш compile эмитил <dataSet xsi:type=...>.
Это вело к двум проблемам:
- сгенерированный XML отличался от платформенного (косметика для bit-perfect)
- skd-decompile симметрично искал <dataSet> и пропускал inner items
при чтении реальных схем — теряя все вложенные fields/titles
Эталон: upload/erf/ПроверкаЭкранирования/.../Templates/СКД_Объединение
показывает что Designer всегда пишет <item xsi:type="..."> внутри Union.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Decompile теперь читает viewMode/itemsViewMode из XML и сохраняет в JSON
точно как было — даже Normal-значения (платформа эмитит эти теги
контекстно, и для bit-perfect нам важно наличие, а не сам режим).
Чтение:
- item-level: selection item, order item (новая object form)
- block-level: selection/filter/order/conditionalAppearance →
XViewMode на settings
- structure group: viewMode + itemsViewMode на самом item
- settings: itemsViewMode
Дополнительно:
- Убран shorthand @normal из filter/condApp/dataParam (Normal — default,
шум в JSON)
- Структурный shorthand "A > B > details" не сворачивается если есть
viewMode/itemsViewMode на элементе
- Selection/order на structure-item сохраняются даже = [Auto] —
compile теперь не дефолтит, поэтому наличие важно для bit-perfect
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
DSL расширения (item-level — паттерн object form расширен):
- selection: {field, viewMode}
- order: {field, direction, viewMode} (новая object form)
- structure group: {type:group, viewMode, itemsViewMode}
DSL расширения (block-level на settings):
- selectionViewMode, filterViewMode, orderViewMode
- conditionalAppearanceViewMode
- itemsViewMode (на самих settings)
Compile эмитит viewMode/itemsViewMode только если явно задано в JSON —
это позволяет decompile сохранить точное наличие/отсутствие из XML и
получить bit-perfect round-trip (платформа эмитит эти теги
контекстно — на ABCXYZ-стиле для каждого блока, а в простых отчётах
без пользовательских настроек — не эмитит).
Дополнительно:
- Пустой LocalStringType теперь эмитится как self-closing (как платформа)
- Убран default order/selection=["Auto"] на StructureItemGroup
(раньше compile дефолтил, теперь эмитит только если задано)
В SKILL.md не упоминаем — фича редкая. Полное описание в spec.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Emit-OutputParameters принудительно использовал str(value), теряя
multilang dict {ru, en} → эмитил как "@{ru=...; en=...}". Теперь
auto-promote ptype=mltext если значение — PSCustomObject/dict.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Найдено через новый debug-tool debug/skd-decompile/verify-roundtrip.ps1
(XML→decompile→compile→diff на сэмпле 30 ERP).
1. Emit-CalcFields appearance: третий дубликат-emitter с тем же
multilang-багом как был в Emit-AppearanceValue для field/cond. Унифи-
цирован через Emit-AppearanceValue. (compile.ps1+py)
2. Emit-SelectionItem: lwsTitle для folder + field-with-title
эмитили "$($item.folder)" — для hashtable получали "@{ru=X; en=Y}".
Унифицирован через Emit-MLText с новой опцией -NoXsiType
(lwsTitle в оригинале без xsi:type, в отличие от <title> в fields).
(compile.ps1+py)
3. Build-Parameter hidden detection: combo availableAsField=false +
useRestriction=true. Только availableAsField=false (без
useRestriction) → object form `availableAsField: false`.
На сэмпле 30 ERP roundtrip-diff: ADDED <content> 667 → 129
(multilang-потери в selection закрыты). Остаточные LOST <item> ~10k —
другие потери (attributeUseRestriction и проч.) — отдельная задача.
Versions: compile v1.35→v1.36, decompile v0.20→v0.21.
1. Внешний .sql теперь именуется <outputBasename>-<datasetName>.sql
(раньше просто <datasetName>.sql). Защищает от коллизий при
batch-decompile нескольких отчётов в одну папку: имена dataset'ов
часто совпадают ("НаборДанных1" — почти везде).
В JSON: "query": "@<outputBasename>-<datasetName>.sql".
2. ConvertTo-CompactJson: Try-InlineJson — пытается сериализовать
container на одну строку. Если результат + текущий indent ≤120
chars → inline; иначе multi-line. Применяется и к объектам и к
массивам (включая массивы из примитивов — раньше они всегда были
inline, что давало гигантские строки на длинных fields).
Примеры inline (объекты ≤120 chars):
- { "value": "B", "style": "header" }
- { "name": "Имя", "expression": "Имя" }
Длинные объекты и массивы — multi-line как раньше.
v0.19 → v0.20.
Заменил ConvertTo-Json (PS5.1) на собственный ConvertTo-CompactJson:
- 2-пробельный indent (вместо 4 + выравнивание keys по длине)
- Массивы примитивов (string/number/bool/null) — inline `[a, b, c]`
- Массивы с объектами/nested arrays — multi-line как раньше
- Кириллица в UTF-8 (без \uXXXX-escapes)
- Корректный escape строк (\\", \\, \n, \r, \t, \uXXXX для control chars)
Build-TotalField: shorthand "name: expr" для любого однострочного
expression. Раньше object form применялась когда expression не Func(arg).
Теперь — только когда есть group или expression многострочный.
Compile принимает любой shorthand вида "dataPath: expression" (Parse-
TotalShorthand делает split по первому ":").
Save-UserStyles тоже использует новый сериализатор.
Все 16 декомпиляционных snapshot'ов обновлены (косметика — JSON
структурно тот же, тесты round-trip проходят).
На реальном отчёте (целевой корпус): 405 → 264 строк (-35%).
v0.18 → v0.19.
Если query ≥3 строк и указан -OutputPath, decompile выносит SQL в
<datasetName>.sql рядом с decompiled.json. В JSON эмитится "@<name>.sql"
вместо inline-строки.
- Имя файла: dataset name (sanitized — non-word chars → _), коллизии
разрешаются суффиксом _2/_3/...
- compile уже поддерживает синтаксис @file.sql (Resolve-QueryValue)
— round-trip симметричен.
- Тесты не изменились: все тестовые queries по 1 строке (порог не
срабатывает).
- На реальных отчётах (ERP/ACC main DCS — 10-50 строк query типично)
даёт значительно более компактный JSON + читаемый .sql с подсветкой
синтаксиса в IDE.
Новый тест dataset-query-multiline (round-trip с внешним .sql).
v0.17 → v0.18.
Закрывает три gap'a, выявленных при полном прогоне ERP+ACC:
1. DataSetFieldFolder — поле-папка для UI-группировки полей в композиторе
настроек. Только dataPath + title, без valueType/role.
- DSL: object form поля с `folder: true`.
- Compile: при folder=true → <field xsi:type="DataSetFieldFolder">.
- Decompile: распознать xsi:type, эмитить object form.
2. GroupItemAuto — пустой <item xsi:type="GroupItemAuto"/> в groupItems
(auto-grouping, аналогично "Auto" в selection).
- DSL: строка "Auto" в groupFields.
- Compile/decompile: round-trip.
3. Empty <field/> в conditionalAppearance/selection (wildcard — apply
to all). Раньше — SelectionItem: sentinel. Теперь эмитим как "Auto"
(семантический эквивалент через SelectedItemAuto).
Новый тест dataset-folder-and-auto-group (round-trip).
Versions: compile v1.34→v1.35, decompile v0.16→v0.17.
На предыдущем прогоне ERP+ACC: 227 sentinel'ов (218 DataSetFieldFolder
+ 7 GroupItemAuto + 2 SelectionItem). После — 0.
Emit-AppearanceValue / emit_appearance_value: hashtable/PSCustomObject/dict
значение → LocalStringType независимо от ключа. Раньше для значения
{ru: "ДЛФ=D", en: "DLF=D"} compile эмитил xs:string "@{ru=ДЛФ=D; en=DLF=D}"
(строковое представление PS hashtable) — потеря структуры и неверный XML.
Wrapper {use: false, value: ...} распознаётся точечно (требуются оба ключа,
чтобы не путать с multilang dict без 'use').
Унификация field-level appearance: parse сохраняет значение как есть
(а не str(v)), emit использует Emit-AppearanceValue вместо дублированной
mini-логики. Side-effect: "true"/"false" в field appearance теперь эмитятся
как xs:boolean (раньше — xs:string). Корректнее для 1С; обновлён один
snapshot теста compile.
Новый тест appearance-multilang-value (поле + conditionalAppearance с
multilang Формат — round-trip bit-perfect).
Versions: compile v1.33→v1.34.
Закрывает п.2 из handoff («известный баг с multilang appearance values»).
Cell в rows теперь может быть либо string ("text"/"{param}"/"|"/">"/null),
либо объектом {value, style: "presetName"}. Object form применяется когда
стиль ячейки отличается от template default.
compile (ps1+py): helpers _get_cell_value / _get_cell_style_or_default.
Emit-AreaTemplateDSL / _emit_area_template_dsl используют per-cell style
для appearance вместо единого template style.
decompile: refactor Build-Template. Первый pass — собрать style name per
cell в cellStyleMap. Второй pass — выбрать template default как most
frequent style. Третий pass — обернуть в {value, style} ячейки, чьи
стили отличаются от default. TemplateStyleMismatch sentinel удалён —
теперь все случаи покрываются через inline override.
Дедуп при обоих pass'ах (Match-PresetByShape) работает через
effectivePresets (built-in + user + ранее аллоцированные customN), так
что одинаковые shape'ы получают одно имя.
Новый тест template-inline-cell-style (round-trip bit-perfect).
Versions: compile v1.32→v1.33, decompile v0.15→v0.16.
Метрики на момент коммита:
- ERP-сэмпл 30: 30/30 clean, 0 sentinel'ов
- Корпус из 40 отчётов целевого класса: 40/40 clean, 0 sentinel'ов
Закрывает категории A, B, C полностью на обоих корпусах.
Категория C — закрыта для однородных шаблонов с custom appearance.
Refactor fingerprint → preset shape (11 полей: font/fontSize/bold/italic/
hAlign/vAlign/wrap/bgColor/textColor/borderColor/borders). vAlign теперь
учитывается в matching (раньше игнорировался).
Алгоритм:
1. При -OutputPath загружается existing skd-styles.json рядом (если есть);
user presets накладываются на built-in по той же логике что и compile.
2. Каждая ячейка → Extract-CellPreset → Match-PresetByShape против
effectivePresets (built-in + user).
3. Если не match — Allocate-CustomStyle: новый customN, регистрируется
в effectivePresets и accumulator.
4. По окончании Save-UserStyles пишет skd-styles.json рядом с outputPath
(preserved existing + новые customN).
5. Compile подхватит файл по своим search-путям (cwd/dirname/scan-up).
В SKILL.md не добавляем (custom стили — для round-trip, не для написания
модель с нуля; built-in `data/header/subheader/total/none` остаются
основным интерфейсом для модели).
- runner.mjs: новый preRun step `writeFile` для подготовки fixture-файлов
в workDir (нужен для теста с предзаписанным skd-styles.json).
- Новый тест template-custom-style: preRun пишет myHeader preset,
скомпилирует темплейт, decompile reverse'ит → переиспользует имя
myHeader (не создаёт customN).
- v0.14 → v0.15.
Метрики:
- ERP-сэмпл 30: 24 → 0 sentinel'ов, clean 26 → 30/30
- Целевой корпус 40 отчётов: 39 → 25 sentinel'ов (часть закрыта), clean
19 → 20/40. Остаточные — шаблоны с разными стилями в разных ячейках
одного шаблона (нужно per-cell style override — отдельная задача).
Закрывает простую часть категории C: шаблоны где у ячеек appearance
содержит только per-cell атрибуты (МинимальнаяШирина и др.) без font/
borders/colors. Раньше такие шаблоны попадали в TemplateStyleMismatch.
- skd-compile (ps1+py): новый preset 'none' со всеми стилевыми полями
null/false. Emit-CellAppearance / _emit_cell_appearance пропускают
Font-элемент когда style.font=null.
- skd-decompile: пустой fingerprint (после отсева per-cell ключей) не
считается за стиль ячейки; если все non-merge ячейки шаблона имели
пустой fp — эмитим style="none" вместо sentinel.
- Новый тест template-no-style (round-trip bit-perfect).
- Versions: compile v1.31→v1.32, decompile v0.13→v0.14.
Метрики:
- ERP-сэмпл 30: 32 → 24 sentinel'ов, clean 24→26/30
- Корпус из 40 отчётов целевого класса: 45 → 39 sentinel'ов, 19/40 clean
Остаточные sentinel'ы — реальный custom appearance (нестандартный шрифт/
выравнивание/цвет вне built-in пресетов). Требует расширения DSL под
hashtable-style — отдельная задача.
Закрывает категорию B полностью на ERP-корпусе:
- selection.folder теперь рекурсивный: внутри items могут быть string,
{field, title}, или ещё одна {folder, items: [...]}. Compile/decompile
обходят дерево рекурсивно (Emit-SelectionItem / Build-SelectionItem).
- structure: новая ветка type=nestedObject с {objectID, settings:
{selection, filter, order, conditionalAppearance, outputParameters}}.
- groupFields теперь объектная форма {field, groupType?, periodAdditionType?}
когда не дефолт (Items / None). Compile уже принимал; decompile перестаёт
ставить warning GroupItemDetails. Try-StructureShorthand игнорирует
object-form поля при сворачивании в строку.
- Refactor: Build-Structure для StructureItemGroup теперь использует
общий Get-GroupFields вместо дублированного inline-кода.
В SKILL.md не добавляем (формы редкие/сложные, модель не пишет с нуля).
Новый тест structure-nested-and-folder покрывает все три случая bit-perfect.
Versions: compile v1.30→v1.31, decompile v0.12→v0.13.
На сэмпле 30 ERP-отчётов: 754 → 32 sentinel'ов (-96%), clean 4 → 24/30.
Остаточные 32 — все TemplateStyleMismatch (категория C, диагностика).
DSL: object-form ключ inputParameters — массив элементов, каждый типизирован
по форме value:
- choiceParameters: [{name, values: [...]}] — параметры выбора (DesignTimeValue)
- choiceParameterLinks: [{name, value, mode}] — связи параметров выбора
- value (+ optional use=false) — простое типизированное значение (bool/string/number)
Compile: Emit-InputParameters / emit_input_parameters → <r:inputParameters>...
Decompile: Read-InputParameters читает любой xsi:type, без SilentDrop warnings.
Build-Parameter — убран вызов несуществующего Check-InputParameters.
В SKILL.md не добавляем (форма сложная — модель не пишет с нуля, но при
декомпиляции из реального отчёта получает корректно и compile примет назад).
Новый тест field-input-parameters (3 типа элементов, bit-perfect round-trip).
Versions: compile v1.29→v1.30, decompile v0.11→v0.12.
На сэмпле 30 ERP-отчётов: SilentDrop:ChoiceParameters/Links 51 → 0,
clean reports 8 → 21, total sentinel'ы 109 → 58.
- Parse-RoleSpec (ps1+py): принимает string ("dim"/"flag1 flag2 K=V") / array / object
- Parse-FieldShorthand: извлекает K=V из shorthand поля (regex \w+=\S+)
- emit: токены → <dcscom:KEY>true</dcscom:KEY>; extras → <dcscom:KEY>VALUE</dcscom:KEY>
(без whitelist; раньше принимались только accountTypeExpression и balanceGroup)
- @period sugar поддерживает override через periodNumber/periodType KV
- Fix имени: balanceGroup в JSON принимается как deprecated alias для balanceGroupName
(в реальном XML 1С элемент называется balanceGroupName; старый код compile эмитил
несуществующий <dcscom:balanceGroup> — ни одного попадания в ERP-корпусе)
- SKILL.md, docs/skd-dsl-spec.md: единое описание четырёх форм роли
- v1.27 → v1.28
- Get-RoleInfo (вместо Get-RoleTokens): любой dcscom:KEY=true → @KEY;
accountTypeExpression/balanceGroup → extras → object form role
(compile уже поддерживает object form через {key: true})
- Build-Field: object form role при наличии extras
- Silent-drop → warnings (без ломания round-trip):
* Check-InputParameters — ChoiceParameters/ChoiceParameterLinks (non-empty)
* orderExpression на field
* scope в conditionalAppearance item
На сэмпле 30 ERP-отчётов: 754 → 147 sentinel'ов (-80%), 8/30 clean.
При наличии __unsupported__ маркеров в JSON (от skd-decompile, Кольцо 2)
compile завершается с exit 4 и понятным сообщением: id/kind/loc каждого
sentinel и подсказка про .warnings.md рядом.
Рекурсивный walk JSON покрывает hashtable/PSCustomObject/array. Sentinel
в любом месте дерева — фейл.
Bump skd-compile v1.26 → v1.27.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sentinel/warnings уже работали по ходу разработки слоёв. Добавил
явные Ring 3 проверки до основного pipeline:
- Picture cells в шаблонах (<dcsat:item xsi:type=Picture>) → exit 3
- Параметры типа ХранилищеЗначения (v8:ValueStorage) → exit 3
- templateCondition (вариативные шаблоны) → exit 3
- Не-DCS корневой элемент → exit 2 (теперь через [Console]::Error,
без обвязки Write-Error и с понятным сообщением по-русски).
Сообщения fail-fast включают рекомендацию использовать /skd-edit
для точечной работы.
Регрессий нет — все 7 синтетических тестов остаются 0 diff.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- DataSetObject: уже была базовая поддержка objectName, теперь fields
тоже извлекаются (отвязали от switch специально для query).
- DataSetUnion: рекурсивный walk вложенных <dataSet> элементов через
Build-DataSet → items[] с полными nested-dataset объектами.
- Вынес логику в Build-DataSet функцию.
Round-trip clean (GUID-normalized) на всех 7 синтетических тестах,
включая ds-types-test с Query + Object + Union в одной схеме.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Парсинг <template>/<template xsi:type=AreaTemplate>/<dcsat:TableRow>/
<dcsat:tableCell> в rows[][].
- Распознавание содержимого ячейки: dcsat:Text → строка, dcsat:Field с
dcscor:Parameter → "{Имя}", dcsat:Field с LocalStringType → строка/multilang,
пустая → null.
- Merge через appearance-флаги ОбъединятьПоВертикали/ОбъединятьПоГоризонтали
на пустых ячейках → "|"/">".
- Детект built-in стилей (header/data/subheader/total) через нормализованный
fingerprint appearance — без сравнения per-cell ширин/высот/merge-флагов.
При несовпадении или неоднородности — sentinel TemplateStyleMismatch.
- Извлечение widths из appearance первого row и minHeight из первой ячейки.
- Drilldown-свёртка: для cells с appearance Расшифровка=Расшифровка_X
и template-параметром DetailsAreaTemplateParameter Расшифровка_X →
свертываем в `{name, expression, drilldown: X}`.
- Сохранение порядка template parameters через [ordered]@{}.
- Fix namespace URI для areatemplate (`area-template` с дефисом).
Bit-perfect round-trip 55924→55924 и 28590→28590 на синтетике с header/data
стилями, merge, drilldown, шаблонными параметрами.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- calculatedFields: shorthand с [title], type, expression и #restrict-флагами;
object form при appearance или multilang title.
- totalFields: детект Func(name) и Func(expr) → shorthand "name: Func"/"name: Func(expr)";
object form при привязке к группе.
- parameters:
- shorthand с [title], type, value, @-флагами;
- распознавание StandardPeriod variants → значение в shorthand;
- @valueList, @hidden флаги;
- availableValues с presentation;
- object form для availableValues/multilang/composite type/expression.
- autoDates-сворачивание: для каждого StandardPeriod-параметра ищем пару
dependent с expression `&P.ДатаНачала`/`&P.ДатаОкончания` (распознаём по
expression, не по имени) и сворачиваем в @autoDates на родителе.
- decimal-тип всегда эмитится с явными (D,F) — JSON читаемее.
- useRestriction суппрессим в параметрах (auto-generated для @hidden).
Bit-perfect round-trip 7468→7468 байт на синтетике
(3 calc + 2 total + 5 параметров включая @autoDates).
Реальный ERP «АнализИзмененийЛичныхДанныхСотрудников» (1035 строк) —
0 warnings при декомпиляции.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Распознавание типов (string/decimal/boolean/date/dateTime/time/CatalogRef.X
и пр.) с qualifiers (decimal-точность/знак, string Length/AllowedLength,
date fractions) → shorthand или composite-массив.
- Роли (@dimension/@account/@balance/@period) с детектом сложных roleAttributes
как sentinel.
- Restrictions (#noField/#noFilter/#noGroup/#noOrder) из useRestriction.
- Multilingual title с авто-сворачиванием {ru:"..."} в строку.
- appearance с поддержкой LocalStringType значений (например, Формат).
- presentationExpression.
- Свёртка дефолтного dataSource (ИсточникДанных1/Local) в умолчание.
- Автодетект object vs shorthand формы поля.
Bit-perfect round-trip на синтетике из 11 разнотипных полей.
Реальный ERP-отчёт АнализВерсийОбъектов декомпилируется с 0 warnings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Убрал ложное обещание «структурной эквивалентности» (DSL покрывает
подмножество СКД). Слил «Гарантии» и «Не поддерживается» в раздел
«Что получаешь» с тремя категориями (покрытое / sentinel / fail-fast).
Добавил Workflow — декомпил это начало процесса, а не финал.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Layer 1 of the skd-decompile plan: SKILL.md with disable-model-invocation,
ps1 skeleton with XML→JSON pipeline, namespace probe for non-DCS root,
sentinel/warnings accumulator, and DataSetQuery extraction (query only).
Test case minimal-query demonstrates round-trip via skd-compile preRun.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Добавляет одну строку-подсказку в add-field: для попадания в Selection
конкретной группировки (а не variant) — связка -NoSelection + add-selection
с @group=. Это уже работало, но не было явно зафиксировано в SKILL.md.
Расширять сам add-field параметром -Group/@group= не стали — текущий
двухкомандный идиом более атомарен и не создаёт edge cases вроде
взаимодействия @group= и -NoSelection.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Раньше PS1-порт делал `Join-Path (Get-Location) $OutFile` без проверки,
что приводило к невалидным склейкам типа `C:\cwd\C:\abs\path.txt`, и
запись падала с «The given path's format is not supported».
Теперь: если путь абсолютный — нормализуется через `Path::GetFullPath`,
если относительный — резолвится против CWD. Python-порт уже был корректен,
только version bump.
Дополнительно: `args_extra` в runner.mjs теперь поддерживает подстановку
`{workDir}` — нужно для тестов с абсолютными путями внутри workspace.
Тесты: `skd-info/outfile-absolute-cyrillic` (PS + Python).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
patch-query теперь нормализует CRLF/CR → LF в old/new/query перед поиском,
поэтому многострочные шаблоны с любым стилем переводов строк находятся
корректно (XmlDocument декодирует text-узлы как LF).
При not-found вместо сухого сообщения выводится воронка диагностики:
1) cross-dataset probe — «Found in dataset 'Y' instead — wrong -DataSet?»
2) tolerant probe (collapse whitespace + NBSP) — «would match with
whitespace normalized» + точка расхождения
3) prefix divergence — «matched N of M chars, expected 'X' (U+...) but
got 'Y' (U+...)» + короткий контекст
Тесты: 4 новых кейса (positive CRLF-tolerant + 3 диагностических negative).
Регрессия 45/45 PS + 45/45 Python.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Calibrated against live Designer output in upload/erf/ПроверкаЭкранирования.
- New type 'time' (synonym 'время'): xs:dateTime with DateFractions=Time
for time-of-day values. Designer uses the same xs:dateTime XSD type as
date/dateTime — only DateFractions differs. Empty value: typed-zero
0001-01-01T00:00:00 (same as dateTime).
- Extended string regex to accept (N,fix) → AllowedLength=Fixed (was
Variable-only). Non-empty fixed-string values are emitted as-given
without space-padding to Length — the platform handles padding on save.
- Composite types in parameters (array of types in object form, e.g.
["string(10,fix)", "CatalogRef.X"]) now work end-to-end: valueType
emits each type with its qualifiers, and empty composite values
serialize as <value xsi:nil="true"/> matching Designer.
Test case empty-param-values extended with 5 new params covering all
three additions. Snapshot validated by skd-validate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calibrated against 1106 vendor reports (ERP 8.3.24 + БП 8.3.27).
Three categories of false positive removed:
- CalculatedField with empty <expression/> demoted error→warning.
Three legitimate vendor patterns surfaced:
* sibling totalField with same dataPath provides the formula
(used in cancellation-rate and share-percentage reports)
* groupTemplate references the field as group name
* field exists only as a declarative anchor for settingsVariants
Warning preserved so genuinely-missing formulas still surface.
- Duplicate template name demoted error→warning. Vendor configs ship
reports (БазаНормируемыхРасходов/Выручка) with three <template> blocks
named Макет1 — the platform identifies templates by position, not by
<name>. Warning still flags the collision without failing validation.
- comparisonType whitelist extended with NotInHierarchy and
NotInListByHierarchy. Existing list was missing the negated
hierarchy operators used in 20 of the 1106 reports.
Result: 0 false positives across the corpus, all genuine errors still
caught (verified separately against intentionally-broken fixtures).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calibrated against ~868 real ERP/БП reports — three false positives caught:
1. Composite types: <v8:Type>xs:string</v8:Type> followed by
<v8:Type>d4p1:CatalogRef.X</v8:Type> with a single trailing
<v8:StringQualifiers> is a legitimate pattern. Rewritten check to
collect all <v8:Type> and qualifier blocks per <valueType>, then
verify each qualifier has a matching scalar type anywhere in the
block — not necessarily right before it.
2. System types: AccumulationRecordType (and similar enum-like system
types) use the http://v8.1c.ru/8.1/data/enterprise namespace
(without /current-config) and a plain TypeName local name with no
dot. Whitelisted as a second valid namespace for ref-like types.
3. v8: scalar types extended: v8:Null, v8:Type, v8:ValueStorage —
present in real configs as type-less placeholders.
Also reverted SKILL.md change from previous commit (validator details
don't belong in user-facing docs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
skd-validate was purely structural (names/refs/duplicates) and missed an
entire class of bugs that XDTO rejects at db-load-xml — exactly the
kinds of mistakes the LLM (or hand-edits) commonly introduce.
New section 16: valueType structural — each <v8:Type> must have a known
prefix (xs:/v8: or any prefix bound to enterprise/current-config),
qualifier blocks must match their preceding type, and qualifier
internals (Digits/FractionDigits/AllowedSign, Length/AllowedLength,
DateFractions) must use legal tokens.
New section 17: value content — <value xsi:type="dcscor:DesignTimeValue">
rejects literal placeholders ('_') and empty strings, since these are
the exact symptom of the titan team's BUG-2.
5 new fixtures cover: bare-decimal, missing-qualifiers,
qualifier/type mismatch, ref-literal '_', bad AllowedSign token.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Emit-SingleValueType / emit_single_value_type previously required full
decimal(D,F) — anything else fell through to a fallback that produced
invalid <v8:Type>decimal</v8:Type> (no xs: prefix, no qualifiers).
New regex `^decimal(\((\d+)(,(\d+))?(,nonneg)?\))?$` accepts:
- decimal → 10,2,Any (money default — most common 1C intent)
- decimal(N) → N,0,Any (integer)
- decimal(N,nonneg) → N,0,Nonnegative
- decimal(N,M) → as before
- decimal(N,M,nonneg) → as before
Synonyms (число, число(N), etc.) inherit the same forms via Resolve-TypeStr.
Shared Emit-ValueType is called from fields, parameters, and output
parameters — one fix covers all three paths. 3 existing snapshots
regenerated with proper xs:decimal + qualifiers, plus new
decimal-qualifier-defaults test case covering all 5 forms × synonyms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Зеркалим решение из skd-compile: убираем .Replace('"','"') из Esc-Xml
и удаляем post-process, который принудительно ставил " внутри
<query>/<expression>. Реальный Конфигуратор так не пишет — экранирование
было анти-1С-стилем и портило round-trip diff.
Снимок add-calculated-field-restrict обновлён под новый формат.
Кейс preserve-entities-modify-parameter-title удалён: его смысл
инвертировался (теперь проверял бы нормализацию, а не сохранение),
а часть про многострочный xmlns уже покрыта preserve-xmlns-multiline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Конфигуратор внутри текстового контента <query>/<expression> оставляет " сырыми
(проверено на ERP DCS: 1504 raw " против 0 "). Убираем .Replace('"','"')
из esc_xml — теперь round-trip diff против типовых остаётся чистым.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
multilang-base/Template.xml содержал <editFormat xsi:type="v8:LocalStringType">
на <field xsi:type="DataSetFieldField">, что нелегально по XDTO-схеме DCS —
1С Designer падал с "Исключение XDTO" при загрузке через
LoadExternalDataProcessorOrReportFromFiles. Snapshot-тесты этого не ловили
(только byte-equality), а platform-verify (tests/skills/verify-snapshots.mjs)
ронялся на трёх кейсах с этой фикстурой.
Заменил <editFormat> на реалистичный <appearance> блок с вложенным
<dcscor:item xsi:type="dcsset:SettingsParameterValue"> и многоязычным
<dcscor:value> (ru + en) — структура взята из типовой ERP-выгрузки. Это
даёт более правильный test для preserve-unknown-children: <appearance>
содержит вложенный multi-lang xsi:type-узел, который точно прошёл бы
через DOM round-trip с искажениями, если бы _unknownChildren не работал.
preserve-unknown-children-modify-field: shorthand изменён с
"@ignoreNullsInGroups" на "@dimension" (no-op по составу role, но
триггерит rebuild). Прежний @ignoreNullsInGroups без @dimension давал
комбинацию, которую Designer отвергает (ignoreNullsInGroups валиден
только в контексте resource-роли).
39/39 snapshot suite (PS+PY) + 39/39 platform verify через erf-build →
Designer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
В типовых конфигурациях (ERP, БП, ЗУП и т.д.) у полей и параметров обычно
есть мульти-язык title (ru + en, иногда + локализация). До этого modify-field /
modify-parameter / modify-dataParameter, перестраивая элемент через
Build-MLTextXml, оставляли только последнее найденное <v8:content> в ru —
en/uk/kk siblings молча терялись, и при следующей выгрузке Designer
ломал миграцию.
Read-FieldProperties сохраняет полный OuterXml <title> в _rawTitle и
коллекционирует OuterXml неизвестных дочерних элементов
(<editFormat>, <appearance>, кастомные расширения) в _unknownChildren.
Build-FieldFragment эмитит:
* _rawTitle как есть, если user не задал новый title;
* Patch-MLTextRu(_rawTitle, newRu) если user задал ru-override — патчит
только <v8:content> в <v8:lang>ru</v8:lang>, остальные языки сохраняет;
* _unknownChildren в конце поля (после valueType).
modify-parameter аналогично: при title-override проверяет multi-lang
(>1 <v8:item>) и патчит ru через Patch-MLTextRu, иначе ребилдит ru-only.
set-field-role сохраняет нестандартные подэлементы <role> (например
<dcscom:addition>, <dcscom:groupFields>), не входящие в фиксированный
known-children set и не указанные через kv в shorthand.
xmlns-стрип на захваченных OuterXml — лишние декларации (которые сериализаторы
добавляют для standalone-фрагментов) убираются.
PY: lxml etree.tostring по умолчанию включает .tail (whitespace после
закрывающего тега), что приводило к non-idempotent ростy whitespace при
повторных прогонах. Везде добавлен with_tail=False.
Новые тесты с idempotent: true:
* preserve-multilang-modify-field (ru-override на multi-lang title);
* preserve-multilang-modify-parameter (то же для параметра);
* preserve-unknown-children-modify-field (role flag, проверяем что
<editFormat> и en title не теряются).
Общая fixture: multilang-base/Template.xml с полем и параметром,
у каждого ru + en title; поле также имеет <editFormat>.
39/39 PS + 39/39 PY. skd-edit v1.20 -> v1.21.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Targeted follow-ups к round-trip фиксу:
* modify-field больше не теряет <valueType> при перестройке поля —
Read-FieldProperties сохраняет полный OuterXml элемента (StringQualifiers,
NumberQualifiers, DateQualifiers и т.п.), Build-FieldFragment отдаёт его
обратно. Лишние xmlns-декларации, добавляемые сериализатором при
выгрузке поддерева, стрипаются регексом.
* Line-ending convention теперь определяется при load (CRLF vs LF) и
единообразно применяется в финале save. Раньше CreateWhitespace и
Build-*Fragment везде использовали CRLF, что приводило к смешанным
переносам в LF-исходниках (и наоборот) и к non-idempotent выходу
modify-parameter title (run 1 → \n\t\t<title>\r\n... → run 2 →
\r\n\t\t<title>\r\n...).
* PS Insert-BeforeElement переведён на LF; все -join "`r`n" → "`n";
py "\r\n".join → "\n". Конечная нормализация переносов делается в
save в соответствии со script:LineEnding.
* preserve-entities-modify-parameter-title.json теперь idempotent: true
(после фикса CRLF leak'а двойной прогон byte-identical).
На реальной схеме diff после modify-field составил 30 строк: целевая
вставка title плюс полезная одноразовая коррекция ранее повреждённых
" в text-content <dcsat:expression>. modify-field идемпотентен.
skd-edit v1.19 -> v1.20.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>