feat(form-decompile,form-compile): частичная/минимальная форма ListSettings (дескриптор settings.listSettings)

Раундтрип TOTAL 21→0, match 153→156. Компилятор всегда эмитил ПОЛНЫЙ каноничный
скелет <ListSettings> (filter+order+conditionalAppearance+itemsViewMode+
itemsUserSettingID), а ~7% форм имеют частичный (напр. только <filter> с
userSettingID) → лишние контейнеры = ADDED.

- Декомпилятор: Get-ListSettingsShape фиксирует «форму» скелета в
  settings.listSettings (ordered-карта present top-level: filter/order/
  conditionalAppearance → блок-мета 'v'/'u'/'vu'/''; itemsViewMode/
  itemsUserSettingID → true). Дескриптор пишется ТОЛЬКО для не-каноничных форм
  ($null для полного канона и неподдержанных top-level item/dataParameters/…).
- Компилятор: при наличии дескриптора эмитит ТОЛЬКО указанные части (контент из
  settings.filter/order/CA, блок-мета из дескриптора); иначе — полный канон
  (без изменений). Аддитивно, дескриптор-gated → 93% канон-форм не затронуты.

Зеркало py. Формы ОстаткиАлкогольнойПродукцииЕГАИС (filter-only) и
ОстаткиПартийЗЕРНО (filter+order) → ListSettings бит-в-бит. Регресс 39/39
(канон-путь). Партиал-путь — harness + provenance (подмножество канона).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-09 16:51:14 +03:00
co-authored by Claude Opus 4.8
parent cc8b283f1b
commit 1ed23b2a08
4 changed files with 83 additions and 14 deletions
@@ -1,4 +1,4 @@
# form-compile v1.92 — Compile 1C managed form from JSON or object metadata
# form-compile v1.93 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -4711,11 +4711,28 @@ function Emit-Attributes {
# Нет items → контейнеры всё равно эмитятся (blockMeta) = каноничный пустой скелет платформы.
$lsi = "$si`t"
X "$si<ListSettings>"
Emit-Filter -items $st.filter -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_FILTER_ID
Emit-Order -items $st.order -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_ORDER_ID
Emit-ConditionalAppearance -items $st.conditionalAppearance -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_CA_ID
X "$lsi<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>"
X "$lsi<dcsset:itemsUserSettingID>$($script:CANON_ITEMS_ID)</dcsset:itemsUserSettingID>"
if ($st.PSObject.Properties['listSettings'] -and $null -ne $st.listSettings) {
# Частичная/минимальная форма скелета — эмитим ТОЛЬКО указанные части с их блок-метой.
# meta: 'v'=viewMode, 'u'=userSettingID (контейнеры); itemsViewMode/itemsUserSettingID → present.
foreach ($prop in $st.listSettings.PSObject.Properties) {
$tag = $prop.Name; $meta = "$($prop.Value)"
$bvm = if ($meta -match 'v') { 'Normal' } else { $null }
switch ($tag) {
'filter' { $bus = if ($meta -match 'u') { $script:CANON_FILTER_ID } else { $null }; Emit-Filter -items $st.filter -indent $lsi -blockViewMode $bvm -blockUserSettingID $bus }
'order' { $bus = if ($meta -match 'u') { $script:CANON_ORDER_ID } else { $null }; Emit-Order -items $st.order -indent $lsi -blockViewMode $bvm -blockUserSettingID $bus }
'conditionalAppearance' { $bus = if ($meta -match 'u') { $script:CANON_CA_ID } else { $null }; Emit-ConditionalAppearance -items $st.conditionalAppearance -indent $lsi -blockViewMode $bvm -blockUserSettingID $bus }
'itemsViewMode' { X "$lsi<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>" }
'itemsUserSettingID' { X "$lsi<dcsset:itemsUserSettingID>$($script:CANON_ITEMS_ID)</dcsset:itemsUserSettingID>" }
}
}
} else {
# Полный каноничный скелет (умолчание, ~93% форм) — без изменений.
Emit-Filter -items $st.filter -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_FILTER_ID
Emit-Order -items $st.order -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_ORDER_ID
Emit-ConditionalAppearance -items $st.conditionalAppearance -indent $lsi -blockViewMode 'Normal' -blockUserSettingID $script:CANON_CA_ID
X "$lsi<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>"
X "$lsi<dcsset:itemsUserSettingID>$($script:CANON_ITEMS_ID)</dcsset:itemsUserSettingID>"
}
X "$si</ListSettings>"
X "$inner</Settings>"
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.92 — Compile 1C managed form from JSON or object metadata
# form-compile v1.93 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -4414,11 +4414,32 @@ def emit_attributes(lines, attrs, indent):
# Нет items → контейнеры всё равно эмитятся (blockMeta) = каноничный пустой скелет платформы.
lsi = f'{si}\t'
lines.append(f'{si}<ListSettings>')
emit_filter(lines, s.get('filter'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_FILTER_ID)
emit_order(lines, s.get('order'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_ORDER_ID)
emit_conditional_appearance(lines, s.get('conditionalAppearance'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_CA_ID)
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
ls_shape = s.get('listSettings')
if ls_shape is not None:
# Частичная/минимальная форма скелета — эмитим ТОЛЬКО указанные части с их блок-метой.
for tag, meta in ls_shape.items():
meta = str(meta)
bvm = 'Normal' if 'v' in meta else None
if tag == 'filter':
bus = CANON_FILTER_ID if 'u' in meta else None
emit_filter(lines, s.get('filter'), lsi, block_view_mode=bvm, block_user_setting_id=bus)
elif tag == 'order':
bus = CANON_ORDER_ID if 'u' in meta else None
emit_order(lines, s.get('order'), lsi, block_view_mode=bvm, block_user_setting_id=bus)
elif tag == 'conditionalAppearance':
bus = CANON_CA_ID if 'u' in meta else None
emit_conditional_appearance(lines, s.get('conditionalAppearance'), lsi, block_view_mode=bvm, block_user_setting_id=bus)
elif tag == 'itemsViewMode':
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
elif tag == 'itemsUserSettingID':
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
else:
# Полный каноничный скелет (умолчание, ~93% форм) — без изменений.
emit_filter(lines, s.get('filter'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_FILTER_ID)
emit_order(lines, s.get('order'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_ORDER_ID)
emit_conditional_appearance(lines, s.get('conditionalAppearance'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_CA_ID)
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
lines.append(f'{si}</ListSettings>')
lines.append(f'{inner}</Settings>')