diff --git a/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 b/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 index 27bbda9f..bdbcd9cf 100644 --- a/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 +++ b/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 @@ -1,4 +1,4 @@ -# db-load-xml v1.1 — Load 1C configuration from XML files +# db-load-xml v1.3 — Load 1C configuration from XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills <# .SYNOPSIS @@ -98,7 +98,10 @@ param( [string]$Format = "Hierarchical", [Parameter(Mandatory=$false)] - [switch]$UpdateDB + [switch]$UpdateDB, + + [Parameter(Mandatory=$false)] + [switch]$StrictLog ) $OutputEncoding = [System.Text.Encoding]::UTF8 @@ -213,20 +216,58 @@ try { $process = Start-Process -FilePath $V8Path -ArgumentList $arguments -NoNewWindow -Wait -PassThru $exitCode = $process.ExitCode + # --- Read log --- + $logContent = $null + if (Test-Path $outFile) { + $logContent = Get-Content $outFile -Raw -ErrorAction SilentlyContinue + } + + # --- Scan log for silent rejections --- + # Platform often writes load-time rejections into /Out but exits with code 0. + # These patterns flag cases where metadata was dropped or rejected silently. + $fatalLogPatterns = @( + 'Неверное свойство объекта метаданных', + 'не входит в состав объекта метаданных', + 'Неизвестное имя типа', + 'Неизвестный объект метаданных', + 'Ни один из документов не является регистратором для регистра', + 'Неверное значение перечисления', + 'не может быть приведен к типу' + ) + $silentFailures = @() + if ($logContent) { + foreach ($line in ($logContent -split "`r?`n")) { + foreach ($pat in $fatalLogPatterns) { + if ($line -match [regex]::Escape($pat)) { + $silentFailures += $line.Trim() + break + } + } + } + } + # --- Result --- + # Default: mirror platform's verdict via exit code. Log content (including any + # rejection warnings) is always printed to stdout for visibility. With -StrictLog, + # elevate exit code to 1 when rejection patterns are found even if platform said 0. if ($exitCode -eq 0) { Write-Host "Load completed successfully" -ForegroundColor Green } else { Write-Host "Error loading configuration (code: $exitCode)" -ForegroundColor Red } - if (Test-Path $outFile) { - $logContent = Get-Content $outFile -Raw -ErrorAction SilentlyContinue - if ($logContent) { - Write-Host "--- Log ---" - Write-Host $logContent - Write-Host "--- End ---" - } + if ($logContent) { + Write-Host "--- Log ---" + Write-Host $logContent + Write-Host "--- End ---" + } + + if ($silentFailures.Count -gt 0) { + $msg = "[warning] log contains $($silentFailures.Count) rejection(s) — platform loaded config but dropped properties/refs" + if (-not $StrictLog) { $msg += " (pass -StrictLog to treat as error)" } + Write-Host $msg -ForegroundColor Yellow + foreach ($f in $silentFailures) { Write-Host " $f" -ForegroundColor Yellow } + if ($StrictLog -and $exitCode -eq 0) { $exitCode = 1 } } exit $exitCode diff --git a/.claude/skills/db-load-xml/scripts/db-load-xml.py b/.claude/skills/db-load-xml/scripts/db-load-xml.py index 60529ad3..e51286b2 100644 --- a/.claude/skills/db-load-xml/scripts/db-load-xml.py +++ b/.claude/skills/db-load-xml/scripts/db-load-xml.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-load-xml v1.1 — Load 1C configuration from XML files +# db-load-xml v1.3 — Load 1C configuration from XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -63,6 +63,11 @@ def main(): help="File format (default: Hierarchical)", ) parser.add_argument("-UpdateDB", action="store_true", help="Also update database configuration after load") + parser.add_argument( + "-StrictLog", + action="store_true", + help="Treat silent rejection warnings in the log as errors (elevate exit code to 1)", + ) args = parser.parse_args() # --- Resolve V8Path --- @@ -157,22 +162,60 @@ def main(): ) exit_code = result.returncode + # --- Read log --- + log_content = "" + if os.path.isfile(out_file): + try: + with open(out_file, "r", encoding="utf-8-sig") as f: + log_content = f.read() + except Exception: + log_content = "" + + # --- Scan log for silent rejections --- + # Platform often writes load-time rejections into /Out but exits with code 0. + # These patterns flag cases where metadata was dropped or rejected silently. + fatal_log_patterns = [ + "Неверное свойство объекта метаданных", + "не входит в состав объекта метаданных", + "Неизвестное имя типа", + "Неизвестный объект метаданных", + "Ни один из документов не является регистратором для регистра", + "Неверное значение перечисления", + "не может быть приведен к типу", + ] + silent_failures = [] + if log_content: + for line in log_content.splitlines(): + for pat in fatal_log_patterns: + if pat in line: + silent_failures.append(line.strip()) + break + # --- Result --- + # Default: mirror platform's verdict via exit code. Log content (including any + # rejection warnings) is always printed to stdout for visibility. With -StrictLog, + # elevate exit code to 1 when rejection patterns are found even if platform said 0. if exit_code == 0: print("Load completed successfully") else: print(f"Error loading configuration (code: {exit_code})", file=sys.stderr) - if os.path.isfile(out_file): - try: - with open(out_file, "r", encoding="utf-8-sig") as f: - log_content = f.read() - if log_content: - print("--- Log ---") - print(log_content) - print("--- End ---") - except Exception: - pass + if log_content: + print("--- Log ---") + print(log_content) + print("--- End ---") + + if silent_failures: + suffix = "" if args.StrictLog else " (pass -StrictLog to treat as error)" + print( + f"[warning] log contains {len(silent_failures)} rejection(s) — " + f"platform loaded config but dropped properties/refs{suffix}", + file=sys.stderr, + ) + for f in silent_failures: + print(f" {f}", file=sys.stderr) + if args.StrictLog and exit_code == 0: + exit_code = 1 sys.exit(exit_code) diff --git a/tests/skills/verify-snapshots.mjs b/tests/skills/verify-snapshots.mjs index 12dde8ce..87a9313f 100644 --- a/tests/skills/verify-snapshots.mjs +++ b/tests/skills/verify-snapshots.mjs @@ -520,7 +520,7 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) { try { execSkill(opts.runtime, 'db-load-xml/scripts/db-load-xml', - ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', baseConfigDir], 180_000); + ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', baseConfigDir, '-StrictLog'], 180_000); log('db-load-xml (config)', true); } catch (e) { const detail = (e.stderr || e.stdout || e.message).trim(); @@ -551,7 +551,7 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) { if (existsSync(extDir)) { try { execSkill(opts.runtime, 'db-load-xml/scripts/db-load-xml', - ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', extDir, '-Extension', extName], 180_000); + ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', extDir, '-Extension', extName, '-StrictLog'], 180_000); log('db-load-xml (ext)', true); } catch (e) { const detail = (e.stderr || e.stdout || e.message).trim(); @@ -624,7 +624,7 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) { try { execSkill(opts.runtime, 'db-load-xml/scripts/db-load-xml', - ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', configDir], 180_000); + ['-V8Path', opts.v8ctx.v8path, '-InfoBasePath', dbDir, '-ConfigDir', configDir, '-StrictLog'], 180_000); log('db-load-xml', true); } catch (e) { const detail = (e.stderr || e.stdout || e.message).trim();