fix(meta-compile,meta-validate,cf-init,*-validate): промежуточные версии формата 2.18/2.19

Версия формата выгрузки идёт лестницей, а не скачком 2.17 -> 2.20:
8.3.20-8.3.24 -> 2.17, 8.3.25 -> 2.18, 8.3.26 -> 2.19, 8.3.27 -> 2.20.
Прежняя дельта мерилась через две ступени и приписала версии 2.20 всё,
что появилось по дороге.

Замер на одной и той же конфигурации, выгруженной четырьмя платформами:
- 2.18: TypeReductionMode (4793 файла), TextToSpeech, 3 редких свойства форм;
- 2.19: новых тегов нет, роли перешли на omit-on-default;
- 2.20: только LineNumberLength.

meta-compile: единый гейт isFormat220 управлял обоими свойствами, поэтому на
проекте 2.18/2.19 TypeReductionMode не эмитился, хотя платформа этих версий
его пишет — роундтрип разъезжался. Порог расщеплён: >=2.18 для
TypeReductionMode, >=2.20 для LineNumberLength.

meta-validate: реестр versionedProps объявлял TypeReductionMode свойством
2.20, из-за чего проверка 18 давала ложную ошибку на корректном файле
2.18/2.19. Исправлено на 2.18.

Валидаторы (meta/form/cf/cfe/epf) считали 2.18 и 2.19 неизвестными версиями
и предупреждали «Unusual version»; cf-init не давал отскаффолдить
конфигурацию под 8.3.25/8.3.26. Обе версии впущены.

Тесты: фикстура empty-config-218, кейс meta-compile на границу свойств
(TypeReductionMode есть, LineNumberLength нет), два кейса meta-validate на
законность штампов 2.18/2.19; кейс error-220-props-in-217 теперь проверяет
оба сообщения с разными порогами. Кейс 2.18 проверен загрузкой в живую
8.3.25 через verify-snapshots.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-01 16:28:50 +03:00
co-authored by Claude Opus 5
parent ecd289fe11
commit 2e5289a88f
30 changed files with 2079 additions and 53 deletions
+4 -4
View File
@@ -1,4 +1,4 @@
# cf-init v1.3 — Create empty 1C configuration scaffold
# cf-init v1.4 — Create empty 1C configuration scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -9,9 +9,9 @@ param(
[string]$Vendor,
[string]$CompatibilityMode = "Version8_3_24",
# Версия формата выгрузки (MDClasses). Её задаёт ПЛАТФОРМА, которой выгружают, и от режима
# совместимости она не зависит: 8.3.24 пишет 2.17, 8.3.27 — 2.20. Дефолт консервативный —
# 2.17 читается всеми поддерживаемыми платформами.
[ValidateSet("2.17", "2.20", "2.21")]
# совместимости она не зависит: 8.3.20-8.3.24 пишут 2.17, 8.3.25 — 2.18, 8.3.26 — 2.19,
# 8.3.27 — 2.20. Дефолт консервативный: 2.17 читается всеми поддерживаемыми платформами.
[ValidateSet("2.17", "2.18", "2.19", "2.20", "2.21")]
[string]$FormatVersion = "2.17"
)
+5 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cf-init v1.3 — Create empty 1C configuration scaffold
# cf-init v1.4 — Create empty 1C configuration scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Generates minimal XML source files for a 1C configuration."""
import sys, os, argparse, uuid
@@ -25,8 +25,10 @@ def main():
parser.add_argument('-Vendor', dest='Vendor', default='')
parser.add_argument('-CompatibilityMode', dest='CompatibilityMode', default='Version8_3_24')
# Версия формата выгрузки (MDClasses) — её задаёт ПЛАТФОРМА, а не режим совместимости:
# 8.3.24 пишет 2.17, 8.3.27 — 2.20. Дефолт консервативный: 2.17 читается всеми платформами.
parser.add_argument('-FormatVersion', dest='FormatVersion', default='2.17', choices=['2.17', '2.20', '2.21'])
# 8.3.20-8.3.24 пишут 2.17, 8.3.25 — 2.18, 8.3.26 — 2.19, 8.3.27 — 2.20.
# Дефолт консервативный: 2.17 читается всеми платформами.
parser.add_argument('-FormatVersion', dest='FormatVersion', default='2.17',
choices=['2.17', '2.18', '2.19', '2.20', '2.21'])
args = parser.parse_args()
name = args.Name
@@ -1,4 +1,4 @@
# cf-validate v1.4 — Validate 1C configuration root structure
# cf-validate v1.5 — Validate 1C configuration root structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -205,8 +205,9 @@ if ($root.NamespaceURI -ne $expectedNs) {
$version = $root.GetAttribute("version")
if (-not $version) {
Report-Warn "1. Missing version attribute on MetaDataObject"
} elseif ($version -ne "2.17" -and $version -ne "2.20" -and $version -ne "2.21") {
Report-Warn "1. Unusual version '$version' (expected 2.17, 2.20 or 2.21)"
} elseif ($version -notin @("2.17", "2.18", "2.19", "2.20", "2.21")) {
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
Report-Warn "1. Unusual version '$version' (expected 2.17-2.20 or 2.21)"
}
# Must have Configuration child
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cf-validate v1.4 — Validate 1C configuration XML structure
# cf-validate v1.5 — Validate 1C configuration XML structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates Configuration.xml: root structure, InternalInfo, properties, ChildObjects, languages."""
import sys, os, argparse, re
@@ -232,8 +232,9 @@ def main():
version = root.get('version', '')
if not version:
r.warn('1. Missing version attribute on MetaDataObject')
elif version not in ('2.17', '2.20', '2.21'):
r.warn(f"1. Unusual version '{version}' (expected 2.17, 2.20 or 2.21)")
elif version not in ('2.17', '2.18', '2.19', '2.20', '2.21'):
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
r.warn(f"1. Unusual version '{version}' (expected 2.17-2.20 or 2.21)")
# Must have Configuration child
cfg_node = None
@@ -1,4 +1,4 @@
# cfe-validate v1.4 — Validate 1C configuration extension structure (CFE)
# cfe-validate v1.5 — Validate 1C configuration extension structure (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -197,8 +197,9 @@ if ($root.NamespaceURI -ne $expectedNs) {
$version = $root.GetAttribute("version")
if (-not $version) {
Report-Warn "1. Missing version attribute on MetaDataObject"
} elseif ($version -ne "2.17" -and $version -ne "2.20" -and $version -ne "2.21") {
Report-Warn "1. Unusual version '$version' (expected 2.17, 2.20 or 2.21)"
} elseif ($version -notin @("2.17", "2.18", "2.19", "2.20", "2.21")) {
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
Report-Warn "1. Unusual version '$version' (expected 2.17-2.20 or 2.21)"
}
# Must have Configuration child
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cfe-validate v1.4 — Validate 1C configuration extension XML structure (CFE)
# cfe-validate v1.5 — Validate 1C configuration extension XML structure (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates extension Configuration.xml: root, InternalInfo, extension properties, ChildObjects, borrowed objects."""
import sys, os, argparse, re
@@ -216,8 +216,9 @@ def main():
version = root.get('version', '')
if not version:
r.warn('1. Missing version attribute on MetaDataObject')
elif version not in ('2.17', '2.20', '2.21'):
r.warn(f"1. Unusual version '{version}' (expected 2.17, 2.20 or 2.21)")
elif version not in ('2.17', '2.18', '2.19', '2.20', '2.21'):
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
r.warn(f"1. Unusual version '{version}' (expected 2.17-2.20 or 2.21)")
# Must have Configuration child
cfg_node = None
@@ -1,4 +1,4 @@
# epf-validate v1.2 — Validate 1C external data processor / report structure
# epf-validate v1.3 — Validate 1C external data processor / report structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# Works for both EPF (ExternalDataProcessor) and ERF (ExternalReport) — auto-detects
param(
@@ -185,8 +185,9 @@ if ($root.NamespaceURI -ne $expectedNs) {
$version = $root.GetAttribute("version")
if (-not $version) {
Report-Warn "1. Missing version attribute on MetaDataObject"
} elseif ($version -ne "2.17" -and $version -ne "2.20" -and $version -ne "2.21") {
Report-Warn "1. Unusual version '$version' (expected 2.17, 2.20 or 2.21)"
} elseif ($version -notin @("2.17", "2.18", "2.19", "2.20", "2.21")) {
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
Report-Warn "1. Unusual version '$version' (expected 2.17-2.20 or 2.21)"
}
# Detect type: ExternalDataProcessor or ExternalReport
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# epf-validate v1.2 — Validate 1C external data processor / report structure
# epf-validate v1.3 — Validate 1C external data processor / report structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# Works for both EPF (ExternalDataProcessor) and ERF (ExternalReport) — auto-detects
@@ -165,8 +165,9 @@ def main():
version = root.get("version", "")
if not version:
report_warn("1. Missing version attribute on MetaDataObject")
elif version not in ("2.17", "2.20", "2.21"):
report_warn(f"1. Unusual version '{version}' (expected 2.17, 2.20 or 2.21)")
elif version not in ("2.17", "2.18", "2.19", "2.20", "2.21"):
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
report_warn(f"1. Unusual version '{version}' (expected 2.17-2.20 or 2.21)")
# Detect type
child_elements = []
@@ -1,4 +1,4 @@
# form-validate v1.8 — Validate 1C managed form
# form-validate v1.9 — Validate 1C managed form
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -127,10 +127,11 @@ if ($root.LocalName -ne "Form") {
Report-Error "Root element is '$($root.LocalName)', expected 'Form'"
} else {
$version = $root.GetAttribute("version")
if ($version -eq "2.17" -or $version -eq "2.20") {
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
if ($version -in @("2.17", "2.18", "2.19", "2.20")) {
Report-OK "Root element: Form version=$version"
} elseif ($version) {
Report-Warn "Form version='$version' (expected 2.17 or 2.20)"
Report-Warn "Form version='$version' (expected 2.17-2.20)"
} else {
Report-Warn "Form version attribute missing"
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-validate v1.8 — Validate 1C managed form
# form-validate v1.9 — Validate 1C managed form
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -161,10 +161,11 @@ def main():
report_error(f"Root element is '{localname(root)}', expected 'Form'")
else:
version = root.get("version", "")
if version in ("2.17", "2.20"):
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
if version in ("2.17", "2.18", "2.19", "2.20"):
report_ok(f"Root element: Form version={version}")
elif version:
report_warn(f"Form version='{version}' (expected 2.17 or 2.20)")
report_warn(f"Form version='{version}' (expected 2.17-2.20)")
else:
report_warn("Form version attribute missing")
@@ -1,4 +1,4 @@
# meta-compile v1.68 — Compile 1C metadata object from JSON
# meta-compile v1.69 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1321,9 +1321,9 @@ function Emit-StandardAttribute {
X "$indent`t<xr:MultiLine>false</xr:MultiLine>"
X "$indent`t<xr:FillFromFillingValue>$ffv</xr:FillFromFillingValue>"
X "$indent`t<xr:CreateOnInput>Auto</xr:CreateOnInput>"
# Формат 2.20 (8.3.27): режим приведения типов. Платформа пишет его КАЖДОМУ стандартному
# Формат 2.18 (8.3.25): режим приведения типов. Платформа пишет его КАЖДОМУ стандартному
# реквизиту; значение всегда TransformValues, кроме владельца (Owner) — там Deny.
if ($script:isFormat220) {
if ($script:isFormat218) {
$trm = OvOr 'TypeReductionMode' $(if ($attrName -ceq 'Owner') { 'Deny' } else { 'TransformValues' })
X "$indent`t<xr:TypeReductionMode>$trm</xr:TypeReductionMode>"
}
@@ -1955,9 +1955,9 @@ function Emit-Attribute {
X "$indent`t`t<DataHistory>$dh</DataHistory>"
}
}
# Формат 2.20 (8.3.27): режим приведения типов — последним в Properties и ТОЛЬКО у измерений
# Формат 2.18 (8.3.25): режим приведения типов — последним в Properties и ТОЛЬКО у измерений
# регистра сведений (у реквизитов/ресурсов и у прочих семейств регистров платформа его не пишет).
if ($script:isFormat220 -and $elemTag -eq "Dimension" -and $context -eq "register-info") {
if ($script:isFormat218 -and $elemTag -eq "Dimension" -and $context -eq "register-info") {
$trm = if ($parsed.typeReductionMode) { "$($parsed.typeReductionMode)" } else { "TransformValues" }
X "$indent`t`t<TypeReductionMode>$trm</TypeReductionMode>"
}
@@ -4043,7 +4043,10 @@ function Get-FormatRank([string]$ver) {
$script:formatVersion = Detect-FormatVersion $OutputDir
$script:compatMode = Detect-CompatibilityMode $OutputDir
# Формат 2.20+ (платформа 8.3.27) — только тогда эмитим новые свойства.
# У каждого свойства свой порог: <TypeReductionMode> появился в формате 2.18 (платформа 8.3.25),
# <LineNumberLength> — в 2.20 (8.3.27). Один общий флаг здесь неверен: на проекте 2.18/2.19 он
# глушил бы TypeReductionMode, который платформа этих версий пишет, и роундтрип бы разъезжался.
$script:isFormat218 = (Get-FormatRank $script:formatVersion) -ge 218
$script:isFormat220 = (Get-FormatRank $script:formatVersion) -ge 220
# Дефолт длины номера строки ТЧ: с режима 8.3.27 платформа заводит новые ТЧ с 9 разрядами.
$script:lineNumberLengthDefault = if ((Get-CompatModeRank $script:compatMode) -ge 80327) { 9 } else { 5 }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.68 — Compile 1C metadata object from JSON
# meta-compile v1.69 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1340,9 +1340,9 @@ def emit_standard_attribute(indent, attr_name, ov=None):
X(f'{indent}\t<xr:MultiLine>false</xr:MultiLine>')
X(f'{indent}\t<xr:FillFromFillingValue>{ffv}</xr:FillFromFillingValue>')
X(f'{indent}\t<xr:CreateOnInput>Auto</xr:CreateOnInput>')
# Формат 2.20 (8.3.27): режим приведения типов. Платформа пишет его КАЖДОМУ стандартному
# Формат 2.18 (8.3.25): режим приведения типов. Платформа пишет его КАЖДОМУ стандартному
# реквизиту; значение всегда TransformValues, кроме владельца (Owner) — там Deny.
if is_format_220:
if is_format_218:
trm = ov.get('TypeReductionMode', 'Deny' if attr_name == 'Owner' else 'TransformValues')
X(f'{indent}\t<xr:TypeReductionMode>{trm}</xr:TypeReductionMode>')
X(f'{indent}\t<xr:MaxValue xsi:nil="true"/>')
@@ -2017,9 +2017,9 @@ def emit_attribute(indent, parsed, context, elem_tag='Attribute'):
# DataHistory — not for Chart* types and non-InformationRegister register family
if context not in ('chart', 'register-other', 'register-accum', 'register-calc', 'register-account'):
X(f'{indent}\t\t<DataHistory>{parsed.get("dataHistory") or "Use"}</DataHistory>')
# Формат 2.20 (8.3.27): режим приведения типов — последним в Properties и ТОЛЬКО у измерений
# Формат 2.18 (8.3.25): режим приведения типов — последним в Properties и ТОЛЬКО у измерений
# регистра сведений (у реквизитов/ресурсов и у прочих семейств регистров платформа его не пишет).
if is_format_220 and elem_tag == 'Dimension' and context == 'register-info':
if is_format_218 and elem_tag == 'Dimension' and context == 'register-info':
X(f'{indent}\t\t<TypeReductionMode>{parsed.get("typeReductionMode") or "TransformValues"}</TypeReductionMode>')
X(f'{indent}\t</Properties>')
X(f'{indent}</{elem_tag}>')
@@ -3935,7 +3935,10 @@ def format_rank(ver):
format_version = detect_format_version(output_dir)
compat_mode = detect_compatibility_mode(output_dir)
# Формат 2.20+ (платформа 8.3.27) — только тогда эмитим новые свойства.
# У каждого свойства свой порог: <TypeReductionMode> появился в формате 2.18 (платформа 8.3.25),
# <LineNumberLength> — в 2.20 (8.3.27). Один общий флаг здесь неверен: на проекте 2.18/2.19 он
# глушил бы TypeReductionMode, который платформа этих версий пишет, и роундтрип бы разъезжался.
is_format_218 = format_rank(format_version) >= 218
is_format_220 = format_rank(format_version) >= 220
# Дефолт длины номера строки ТЧ: с режима 8.3.27 платформа заводит новые ТЧ с 9 разрядами.
line_number_length_default = 9 if compat_mode_rank(compat_mode) >= 80327 else 5
@@ -1,4 +1,4 @@
# meta-validate v1.12 — Validate 1C metadata object structure (+корневой <Type>: скаляр без структуры = ошибка)
# meta-validate v1.13 — Validate 1C metadata object structure (+корневой <Type>: скаляр без структуры = ошибка)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -344,8 +344,9 @@ if ($root.NamespaceURI -ne $expectedNs) {
$version = $root.GetAttribute("version")
if (-not $version) {
Report-Warn "1. Missing version attribute on MetaDataObject"
} elseif ($version -ne "2.17" -and $version -ne "2.20") {
Report-Warn "1. Unusual version '$version' (expected 2.17 or 2.20)"
} elseif ($version -notin @("2.17", "2.18", "2.19", "2.20")) {
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
Report-Warn "1. Unusual version '$version' (expected 2.17-2.20)"
}
# Detect type element — exactly one child element in md namespace
@@ -1499,7 +1500,7 @@ if ($script:configDir) {
# рапортует успех, а свойство теряется); (2) подсказать, что конструкция требует более нового
# формата. Расширяется одной строкой на свойство — задел под 2.21 (8.5) и последующие.
$versionedProps = @{
"TypeReductionMode" = "2.20" # режим приведения типов (стандартные реквизиты, измерения РС)
"TypeReductionMode" = "2.18" # режим приведения типов (стандартные реквизиты, измерения РС)
"LineNumberLength" = "2.20" # длина номера строки ТЧ (5..9)
}
# Версия формата как число: "2.20" → 220. Строковое сравнение неверно ("2.9" > "2.17").
@@ -1,4 +1,4 @@
# meta-validate v1.12 — Validate 1C metadata object structure (Python port) (+корневой <Type>: скаляр без структуры = ошибка)
# meta-validate v1.13 — Validate 1C metadata object structure (Python port) (+корневой <Type>: скаляр без структуры = ошибка)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
@@ -371,8 +371,9 @@ if root_ns != expected_ns:
version = root.get("version", "")
if not version:
report_warn("1. Missing version attribute on MetaDataObject")
elif version not in ("2.17", "2.20"):
report_warn(f"1. Unusual version '{version}' (expected 2.17 or 2.20)")
elif version not in ("2.17", "2.18", "2.19", "2.20"):
# Лестница версий формата: 2.17 (8.3.20-8.3.24), 2.18 (8.3.25), 2.19 (8.3.26), 2.20 (8.3.27).
report_warn(f"1. Unusual version '{version}' (expected 2.17-2.20)")
# Detect type element -- exactly one child element in md namespace
type_node = None
@@ -1401,7 +1402,7 @@ if config_dir:
# рапортует успех, а свойство теряется); (2) подсказать, что конструкция требует более нового
# формата. Расширяется одной строкой на свойство — задел под 2.21 (8.5) и последующие.
versioned_props = {
"TypeReductionMode": "2.20", # режим приведения типов (стандартные реквизиты, измерения РС)
"TypeReductionMode": "2.18", # режим приведения типов (стандартные реквизиты, измерения РС)
"LineNumberLength": "2.20", # длина номера строки ТЧ (5..9)
}