diff --git a/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 b/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 index 75405cd7..b8453505 100644 --- a/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 +++ b/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 @@ -1,4 +1,4 @@ -# db-dump-cf v1.6 — Dump 1C configuration to CF file +# db-dump-cf v1.7 — Dump 1C configuration to CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -147,6 +147,13 @@ function Invoke-IbcmdProcess { } +function Test-OutputNonEmpty { + # Postcondition: the platform must have produced a non-empty output file. + # Exit code 0 without it (broken/headless env) is a false success — reject it. + param([string]$Path) + return (Test-Path $Path -PathType Leaf) -and ((Get-Item $Path -ErrorAction SilentlyContinue).Length -gt 0) +} + $engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" } # --- Validate connection --- @@ -187,8 +194,12 @@ try { $__ib = Invoke-IbcmdProcess $V8Path $arguments $output = $__ib.Output $exitCode = $__ib.ExitCode + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Configuration dumped successfully to: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — configuration was not dumped" -ForegroundColor Red } else { Write-Host "Error dumping configuration (code: $exitCode)" -ForegroundColor Red } @@ -229,8 +240,13 @@ try { $exitCode = $process.ExitCode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Configuration dumped successfully to: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — configuration was not dumped" -ForegroundColor Red } else { Write-Host "Error dumping configuration (code: $exitCode)" -ForegroundColor Red } diff --git a/.claude/skills/db-dump-cf/scripts/db-dump-cf.py b/.claude/skills/db-dump-cf/scripts/db-dump-cf.py index 342c5e73..299467e3 100644 --- a/.claude/skills/db-dump-cf/scripts/db-dump-cf.py +++ b/.claude/skills/db-dump-cf/scripts/db-dump-cf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-cf v1.6 — Dump 1C configuration to CF file +# db-dump-cf v1.7 — Dump 1C configuration to CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -99,6 +99,12 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True): return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace") +def output_nonempty(path): + """Postcondition: the platform must have produced a non-empty output file. + Exit code 0 without it (broken/headless env) is a false success — reject it.""" + return os.path.isfile(path) and os.path.getsize(path) > 0 + + def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") @@ -152,15 +158,21 @@ def main(): arguments.append(f"--data={ib_data}") print(f"Running: ibcmd {' '.join(arguments)}") result = run_ibcmd([v8path] + arguments, bool(args.UserName)) - if result.returncode == 0: + exit_code = result.returncode + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 + if exit_code == 0: print(f"Configuration dumped successfully to: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped", file=sys.stderr) else: - print(f"Error dumping configuration (code: {result.returncode})", file=sys.stderr) + print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr) if result.stdout: print(result.stdout) if result.stderr: print(result.stderr, file=sys.stderr) - sys.exit(result.returncode) + sys.exit(exit_code) # --- Temp dir --- temp_dir = os.path.join(tempfile.gettempdir(), f"db_dump_cf_{random.randint(0, 999999)}") @@ -203,8 +215,14 @@ def main(): exit_code = result.returncode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 if exit_code == 0: print(f"Configuration dumped successfully to: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — configuration was not dumped", file=sys.stderr) else: print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr) diff --git a/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 b/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 index b2f5339a..76130588 100644 --- a/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 +++ b/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 @@ -1,4 +1,4 @@ -# db-dump-dt v1.5 — Dump 1C information base to DT file +# db-dump-dt v1.6 — Dump 1C information base to DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -131,6 +131,13 @@ function Invoke-IbcmdProcess { } +function Test-OutputNonEmpty { + # Postcondition: the platform must have produced a non-empty output file. + # Exit code 0 without it (broken/headless env) is a false success — reject it. + param([string]$Path) + return (Test-Path $Path -PathType Leaf) -and ((Get-Item $Path -ErrorAction SilentlyContinue).Length -gt 0) +} + $engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" } # --- Validate connection --- @@ -167,8 +174,12 @@ try { $__ib = Invoke-IbcmdProcess $V8Path $arguments $output = $__ib.Output $exitCode = $__ib.ExitCode + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Information base dumped successfully to: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — information base was not dumped" -ForegroundColor Red } else { Write-Host "Error dumping information base (code: $exitCode)" -ForegroundColor Red } @@ -202,8 +213,13 @@ try { $exitCode = $process.ExitCode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Information base dumped successfully to: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — information base was not dumped" -ForegroundColor Red } else { Write-Host "Error dumping information base (code: $exitCode)" -ForegroundColor Red } diff --git a/.claude/skills/db-dump-dt/scripts/db-dump-dt.py b/.claude/skills/db-dump-dt/scripts/db-dump-dt.py index 7a603c6d..1cb1c9ca 100644 --- a/.claude/skills/db-dump-dt/scripts/db-dump-dt.py +++ b/.claude/skills/db-dump-dt/scripts/db-dump-dt.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-dt v1.5 — Dump 1C information base to DT file +# db-dump-dt v1.6 — Dump 1C information base to DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -99,6 +99,12 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True): return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace") +def output_nonempty(path): + """Postcondition: the platform must have produced a non-empty output file. + Exit code 0 without it (broken/headless env) is a false success — reject it.""" + return os.path.isfile(path) and os.path.getsize(path) > 0 + + def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") @@ -145,15 +151,21 @@ def main(): arguments.append(f"--data={ib_data}") print(f"Running: ibcmd {' '.join(arguments)}") result = run_ibcmd([v8path] + arguments, bool(args.UserName)) - if result.returncode == 0: + exit_code = result.returncode + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 + if exit_code == 0: print(f"Information base dumped successfully to: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped", file=sys.stderr) else: - print(f"Error dumping information base (code: {result.returncode})", file=sys.stderr) + print(f"Error dumping information base (code: {exit_code})", file=sys.stderr) if result.stdout: print(result.stdout) if result.stderr: print(result.stderr, file=sys.stderr) - sys.exit(result.returncode) + sys.exit(exit_code) # --- Temp dir --- temp_dir = os.path.join(tempfile.gettempdir(), f"db_dump_dt_{random.randint(0, 999999)}") @@ -190,8 +202,14 @@ def main(): exit_code = result.returncode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 if exit_code == 0: print(f"Information base dumped successfully to: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — information base was not dumped", file=sys.stderr) else: print(f"Error dumping information base (code: {exit_code})", file=sys.stderr) diff --git a/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 b/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 index 43312610..8508f2b9 100644 --- a/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 +++ b/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 @@ -1,4 +1,4 @@ -# db-dump-xml v1.8 — Dump 1C configuration to XML files +# db-dump-xml v1.9 — Dump 1C configuration to XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -170,6 +170,13 @@ function Invoke-IbcmdProcess { } +function Test-DirNonEmpty { + # Postcondition: the platform must have written files into the output directory. + # Exit code 0 with an empty dir (broken/headless env) is a false success — reject it. + param([string]$Path) + return (Test-Path $Path -PathType Container) -and ([bool](Get-ChildItem -LiteralPath $Path -Force -ErrorAction SilentlyContinue | Select-Object -First 1)) +} + $engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" } # --- Validate connection --- @@ -228,8 +235,12 @@ try { $__ib = Invoke-IbcmdProcess $V8Path $arguments $output = $__ib.Output $exitCode = $__ib.ExitCode + $outMissing = ($exitCode -eq 0) -and -not (Test-DirNonEmpty $ConfigDir) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Configuration exported successfully to: $ConfigDir" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no files under $ConfigDir — configuration was not exported" -ForegroundColor Red } else { Write-Host "Error exporting configuration (code: $exitCode)" -ForegroundColor Red } @@ -298,9 +309,14 @@ try { $exitCode = $process.ExitCode # --- Result --- + # Postcondition: exit 0 with an empty output directory is a false success. + $outMissing = ($exitCode -eq 0) -and -not (Test-DirNonEmpty $ConfigDir) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Dump completed successfully" -ForegroundColor Green Write-Host "Configuration dumped to: $ConfigDir" + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no files under $ConfigDir — configuration was not dumped" -ForegroundColor Red } else { Write-Host "Error dumping configuration (code: $exitCode)" -ForegroundColor Red } diff --git a/.claude/skills/db-dump-xml/scripts/db-dump-xml.py b/.claude/skills/db-dump-xml/scripts/db-dump-xml.py index 6df5bc32..a474b5c7 100644 --- a/.claude/skills/db-dump-xml/scripts/db-dump-xml.py +++ b/.claude/skills/db-dump-xml/scripts/db-dump-xml.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-xml v1.8 — Dump 1C configuration to XML files +# db-dump-xml v1.9 — Dump 1C configuration to XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -99,6 +99,12 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True): return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace") +def dir_nonempty(path): + """Postcondition: the platform must have written files into the output directory. + Exit code 0 with an empty dir (broken/headless env) is a false success — reject it.""" + return os.path.isdir(path) and any(os.scandir(path)) + + def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") @@ -183,15 +189,21 @@ def main(): arguments.append(f"--data={ib_data}") print(f"Running: ibcmd {' '.join(arguments)}") result = run_ibcmd([v8path] + arguments, bool(args.UserName)) - if result.returncode == 0: + exit_code = result.returncode + out_missing = exit_code == 0 and not dir_nonempty(args.ConfigDir) + if out_missing: + exit_code = 1 + if exit_code == 0: print(f"Configuration exported successfully to: {args.ConfigDir}") + elif out_missing: + print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not exported", file=sys.stderr) else: - print(f"Error exporting configuration (code: {result.returncode})", file=sys.stderr) + print(f"Error exporting configuration (code: {exit_code})", file=sys.stderr) if result.stdout: print(result.stdout) if result.stderr: print(result.stderr, file=sys.stderr) - sys.exit(result.returncode) + sys.exit(exit_code) # --- Temp dir --- temp_dir = os.path.join(tempfile.gettempdir(), f"db_dump_xml_{random.randint(0, 999999)}") @@ -257,9 +269,15 @@ def main(): exit_code = result.returncode # --- Result --- + # Postcondition: exit 0 with an empty output directory is a false success. + out_missing = exit_code == 0 and not dir_nonempty(args.ConfigDir) + if out_missing: + exit_code = 1 if exit_code == 0: print("Dump completed successfully") print(f"Configuration dumped to: {args.ConfigDir}") + elif out_missing: + print(f"Error: exit code 0 but no files under {args.ConfigDir} — configuration was not dumped", file=sys.stderr) else: print(f"Error dumping configuration (code: {exit_code})", file=sys.stderr) diff --git a/.claude/skills/epf-build/scripts/epf-build.ps1 b/.claude/skills/epf-build/scripts/epf-build.ps1 index dd32da8a..8ebfd675 100644 --- a/.claude/skills/epf-build/scripts/epf-build.ps1 +++ b/.claude/skills/epf-build/scripts/epf-build.ps1 @@ -1,4 +1,4 @@ -# epf-build v1.6 — Build external data processor or report (EPF/ERF) from XML sources +# epf-build v1.7 — Build external data processor or report (EPF/ERF) from XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -141,6 +141,13 @@ function Invoke-IbcmdProcess { } +function Test-OutputNonEmpty { + # Postcondition: the platform must have produced a non-empty output file. + # Exit code 0 without it (broken/headless env) is a false success — reject it. + param([string]$Path) + return (Test-Path $Path -PathType Leaf) -and ((Get-Item $Path -ErrorAction SilentlyContinue).Length -gt 0) +} + $engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" } if ($engine -eq "ibcmd" -and $InfoBaseServer -and $InfoBaseRef) { Write-Host "Error: ibcmd supports file infobases only (use -InfoBasePath or omit for stub)" -ForegroundColor Red @@ -192,8 +199,12 @@ try { $__ib = Invoke-IbcmdProcess $V8Path $arguments $output = $__ib.Output $exitCode = $__ib.ExitCode + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "External data processor/report built successfully: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — build produced no output" -ForegroundColor Red } else { Write-Host "Error building external data processor/report (code: $exitCode)" -ForegroundColor Red } @@ -227,8 +238,13 @@ try { $exitCode = $process.ExitCode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + $outMissing = ($exitCode -eq 0) -and -not (Test-OutputNonEmpty $OutputFile) + if ($outMissing) { $exitCode = 1 } if ($exitCode -eq 0) { Write-Host "Build completed successfully: $OutputFile" -ForegroundColor Green + } elseif ($outMissing) { + Write-Host "Error: exit code 0 but no non-empty file at $OutputFile — build produced no output" -ForegroundColor Red } else { Write-Host "Error building (code: $exitCode)" -ForegroundColor Red } diff --git a/.claude/skills/epf-build/scripts/epf-build.py b/.claude/skills/epf-build/scripts/epf-build.py index 383f58fb..b124fc5a 100644 --- a/.claude/skills/epf-build/scripts/epf-build.py +++ b/.claude/skills/epf-build/scripts/epf-build.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# epf-build v1.6 — Build external data processor or report (EPF/ERF) from XML sources +# epf-build v1.7 — Build external data processor or report (EPF/ERF) from XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -99,6 +99,12 @@ def run_ibcmd(cmd, has_username=False, warn_no_user=True): return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace") +def output_nonempty(path): + """Postcondition: the platform must have produced a non-empty output file. + Exit code 0 without it (broken/headless env) is a false success — reject it.""" + return os.path.isfile(path) and os.path.getsize(path) > 0 + + def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") @@ -168,15 +174,21 @@ def main(): arguments.append(f"--data={ib_data}") print(f"Running: ibcmd {' '.join(arguments)}") result = run_ibcmd([v8path] + arguments, warn_no_user=False) - if result.returncode == 0: + exit_code = result.returncode + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 + if exit_code == 0: print(f"External data processor/report built successfully: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output", file=sys.stderr) else: - print(f"Error building external data processor/report (code: {result.returncode})", file=sys.stderr) + print(f"Error building external data processor/report (code: {exit_code})", file=sys.stderr) if result.stdout: print(result.stdout) if result.stderr: print(result.stderr, file=sys.stderr) - sys.exit(result.returncode) + sys.exit(exit_code) # --- Build arguments --- arguments = ["DESIGNER"] @@ -208,8 +220,14 @@ def main(): exit_code = result.returncode # --- Result --- + # Postcondition: exit 0 without a non-empty output file is a false success. + out_missing = exit_code == 0 and not output_nonempty(args.OutputFile) + if out_missing: + exit_code = 1 if exit_code == 0: print(f"Build completed successfully: {args.OutputFile}") + elif out_missing: + print(f"Error: exit code 0 but no non-empty file at {args.OutputFile} — build produced no output", file=sys.stderr) else: print(f"Error building (code: {exit_code})", file=sys.stderr)