From b9ed48ccf2c272f37c89e1d9bdea02ad2095680d Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 11 Jul 2026 20:41:53 +0300 Subject: [PATCH] =?UTF-8?q?feat(meta-validate):=20=D0=BF=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=BA=D0=B0=20=D1=81=D1=83=D1=89=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=81=D1=81=D1=8B?= =?UTF-8?q?=D0=BB=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20=D1=82=D0=B8=D0=BF=D0=BE?= =?UTF-8?q?=D0=B2=20(=D0=A4=D0=B0=D0=B7=D0=B0=201.2,=20v1.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check 16: все вида CatalogRef.X / DocumentRef / EnumRef / DefinedType / ChartOf*Ref / BusinessProcessRef / ExchangePlanRef / TaskRef проверяются на наличие объекта в конфигурации (Dir/X.xml или папка Dir/X). WARN-уровень, не error: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаются по маркеру ConfigurationExtensionPurpose — их типы ссылаются на объекты базовой конфигурации, отсутствующие в выгрузке расширения. Дедуп по ref-ключу; маппинг тип→каталог согласован с TYPE_TO_DIR (verify-snapshots). Тесты: valid-reftype-resolves (self-ref, -Detailed, OK) + warn-reftype-missing (WARN, exit 0). meta-validate 18/18 ps1+py. Анти-дрейф OK. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../meta-validate/scripts/meta-validate.ps1 | 55 +++++++++++++++++- .../meta-validate/scripts/meta-validate.py | 57 ++++++++++++++++++- .../meta-validate/valid-reftype-resolves.json | 17 ++++++ .../meta-validate/warn-reftype-missing.json | 15 +++++ 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 tests/skills/cases/meta-validate/valid-reftype-resolves.json create mode 100644 tests/skills/cases/meta-validate/warn-reftype-missing.json diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index fa508288..82acae13 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -1,4 +1,4 @@ -# meta-validate v1.7 — Validate 1C metadata object structure +# meta-validate v1.8 — Validate 1C metadata object structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1391,6 +1391,59 @@ if ($childObjNode) { } } +# --- Check 16: Reference type existence — типы вида CatalogRef.X должны разрешаться в объекты конфигурации --- +# WARN-уровень: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаем — +# их типы ссылаются на объекты базовой конфигурации, которых нет в выгрузке расширения. + +if ($script:configDir) { + $isExtension = $false + $cfgXmlPath = Join-Path $script:configDir "Configuration.xml" + if (Test-Path $cfgXmlPath) { + $cfgContent = [System.IO.File]::ReadAllText($cfgXmlPath, [System.Text.Encoding]::UTF8) + if ($cfgContent.Contains("ConfigurationExtensionPurpose")) { $isExtension = $true } + } + if (-not $isExtension) { + $refDirMap = @{ + "CatalogRef"="Catalogs"; "DocumentRef"="Documents"; "EnumRef"="Enums" + "ChartOfAccountsRef"="ChartsOfAccounts"; "ChartOfCharacteristicTypesRef"="ChartsOfCharacteristicTypes" + "ChartOfCalculationTypesRef"="ChartsOfCalculationTypes"; "BusinessProcessRef"="BusinessProcesses" + "ExchangePlanRef"="ExchangePlans"; "TaskRef"="Tasks"; "DefinedType"="DefinedTypes" + } + $typeNodes = $xmlDoc.SelectNodes("//v8:Type", $ns) + $checkedRefs = @{} # refKey -> $true если найден; для OK-условия + $missingRefs = @{} # refKey -> refDir + foreach ($tn in $typeNodes) { + $tv = $tn.InnerText.Trim() + if (-not $tv) { continue } + $colonIdx = $tv.IndexOf(':') + if ($colonIdx -ge 0) { $tv = $tv.Substring($colonIdx + 1) } + $dotIdx = $tv.IndexOf('.') + if ($dotIdx -lt 0) { continue } + $refCat = $tv.Substring(0, $dotIdx) + $refName = $tv.Substring($dotIdx + 1) + $refDir = $refDirMap[$refCat] + if (-not $refDir -or -not $refName) { continue } + $refKey = "$refCat.$refName" + if ($checkedRefs.ContainsKey($refKey)) { continue } + $refFolder = Join-Path $script:configDir (Join-Path $refDir $refName) + $refFile = Join-Path $script:configDir (Join-Path $refDir "$refName.xml") + if ((Test-Path $refFolder) -or (Test-Path $refFile)) { + $checkedRefs[$refKey] = $true + } else { + $checkedRefs[$refKey] = $false + $missingRefs[$refKey] = $refDir + } + } + if ($missingRefs.Count -gt 0) { + foreach ($mk in ($missingRefs.Keys | Sort-Object)) { + Report-Warn "16. Ссылочный тип '$mk' не найден в конфигурации ($($missingRefs[$mk])/) — при загрузке будет ошибка неизвестного типа" + } + } elseif ($checkedRefs.Count -gt 0) { + Report-OK "16. Reference types: $($checkedRefs.Count) resolved" + } + } +} + # --- Final output --- & $finalize diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index ac0eaa04..e800d269 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -1,4 +1,4 @@ -# meta-validate v1.7 — Validate 1C metadata object structure (Python port) +# meta-validate v1.8 — Validate 1C metadata object structure (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -1302,6 +1302,61 @@ if child_obj_node is not None: if check15_ok and cmd_count > 0: report_ok(f"15. Commands: {cmd_count} command(s), groups valid") +# ── Check 16: Reference type existence — типы вида CatalogRef.X должны разрешаться в объекты конфигурации ── +# WARN-уровень: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаем — +# их типы ссылаются на объекты базовой конфигурации, которых нет в выгрузке расширения. + +if config_dir: + is_extension = False + cfg_xml_path = os.path.join(config_dir, "Configuration.xml") + if os.path.exists(cfg_xml_path): + try: + with open(cfg_xml_path, "r", encoding="utf-8") as f: + cfg_content = f.read() + if "ConfigurationExtensionPurpose" in cfg_content: + is_extension = True + except Exception: + pass + if not is_extension: + ref_dir_map = { + "CatalogRef": "Catalogs", "DocumentRef": "Documents", "EnumRef": "Enums", + "ChartOfAccountsRef": "ChartsOfAccounts", "ChartOfCharacteristicTypesRef": "ChartsOfCharacteristicTypes", + "ChartOfCalculationTypesRef": "ChartsOfCalculationTypes", "BusinessProcessRef": "BusinessProcesses", + "ExchangePlanRef": "ExchangePlans", "TaskRef": "Tasks", "DefinedType": "DefinedTypes", + } + checked_refs = {} # ref_key -> найден ли; для условия OK + missing_refs = {} # ref_key -> ref_dir + for tn in find_all(root, ".//v8:Type"): + tv = inner_text(tn).strip() + if not tv: + continue + colon_idx = tv.find(":") + if colon_idx >= 0: + tv = tv[colon_idx + 1:] + dot_idx = tv.find(".") + if dot_idx < 0: + continue + ref_cat = tv[:dot_idx] + ref_name = tv[dot_idx + 1:] + ref_dir = ref_dir_map.get(ref_cat) + if not ref_dir or not ref_name: + continue + ref_key = f"{ref_cat}.{ref_name}" + if ref_key in checked_refs: + continue + ref_folder = os.path.join(config_dir, ref_dir, ref_name) + ref_file = os.path.join(config_dir, ref_dir, ref_name + ".xml") + if os.path.exists(ref_folder) or os.path.exists(ref_file): + checked_refs[ref_key] = True + else: + checked_refs[ref_key] = False + missing_refs[ref_key] = ref_dir + if missing_refs: + for mk in sorted(missing_refs): + report_warn(f"16. Ссылочный тип '{mk}' не найден в конфигурации ({missing_refs[mk]}/) — при загрузке будет ошибка неизвестного типа") + elif checked_refs: + report_ok(f"16. Reference types: {len(checked_refs)} resolved") + # ── Final output ────────────────────────────────────────────── finalize() diff --git a/tests/skills/cases/meta-validate/valid-reftype-resolves.json b/tests/skills/cases/meta-validate/valid-reftype-resolves.json new file mode 100644 index 00000000..d7680dde --- /dev/null +++ b/tests/skills/cases/meta-validate/valid-reftype-resolves.json @@ -0,0 +1,17 @@ +{ + "name": "Ссылочный тип реквизита разрешается в объект конфигурации (Check 16)", + "setup": "empty-config", + "preRun": [ + { + "script": "meta-compile/scripts/meta-compile", + "input": { "type": "Catalog", "name": "ТестСсылка", "attributes": ["Связанный: CatalogRef.ТестСсылка"] }, + "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" } + } + ], + "params": { "objectPath": "Catalogs/ТестСсылка" }, + "args_extra": ["-Detailed"], + "expect": { + "stdoutContains": "16. Reference types:", + "stdoutNotContains": "16. Ссылочный тип" + } +} diff --git a/tests/skills/cases/meta-validate/warn-reftype-missing.json b/tests/skills/cases/meta-validate/warn-reftype-missing.json new file mode 100644 index 00000000..dd99fc37 --- /dev/null +++ b/tests/skills/cases/meta-validate/warn-reftype-missing.json @@ -0,0 +1,15 @@ +{ + "name": "Валидатор предупреждает о несуществующем ссылочном типе реквизита (Check 16)", + "setup": "empty-config", + "preRun": [ + { + "script": "meta-compile/scripts/meta-compile", + "input": { "type": "Catalog", "name": "ТестСсылкаБитая", "attributes": ["Контрагент: CatalogRef.НетТакогоСправочника"] }, + "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" } + } + ], + "params": { "objectPath": "Catalogs/ТестСсылкаБитая" }, + "expect": { + "stdoutContains": "16. Ссылочный тип 'CatalogRef.НетТакогоСправочника'" + } +}