**Motivations:** - Implémenter le workflow complet de démonstration Collatz (commandes.md) - Permettre la reprise après interruption au palier D20 **Evolutions:** - Scripts 01-12 et run-full-workflow alignés sur commandes.md sections 1-10 - collatz_recover_noyau.py : recréation de noyau_post_D20 à partir du CSV candidats - Option --resume-from D20 dans collatz_k_pipeline pour reprendre sans recalculer D18-D19-F15 - Détection automatique : si candidats_D20 existe sans noyau_post_D20, récupération puis poursuite - Filtres --cible=critique et --modulo dans collatz_fusion_pipeline - ROOT par défaut = collatz_k_scripts (plus data/source vide) **Pages affectées:** - .gitignore (__pycache__, out/) - applications/collatz/collatz_k_scripts/*.py - applications/collatz/scripts/*.sh - applications/collatz/scripts/README.md
91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
collatz_compare_verifiers.py
|
|
|
|
Compare two verification outputs (MD files from verifiers).
|
|
Reports differences.
|
|
|
|
Usage: python collatz_compare_verifiers.py --verif1 MD --verif2 MD --output MD
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
|
|
def parse_verification_output(path: str) -> tuple[str, list[str]]:
|
|
"""Parse verification MD: status (OK/failed) and list of error lines."""
|
|
p = Path(path)
|
|
if not p.exists():
|
|
return "missing", [f"File not found: {path}"]
|
|
|
|
text = p.read_text(encoding="utf-8")
|
|
lines = text.strip().splitlines()
|
|
status = "OK" if "Verification OK" in text else "failed"
|
|
|
|
errors: list[str] = []
|
|
for ln in lines:
|
|
if ln.strip().startswith("- "):
|
|
errors.append(ln.strip()[2:])
|
|
|
|
return status, errors
|
|
|
|
|
|
def compare(verif1: str, verif2: str) -> list[str]:
|
|
"""Compare two verification outputs. Returns list of difference descriptions."""
|
|
diffs: list[str] = []
|
|
|
|
s1, e1 = parse_verification_output(verif1)
|
|
s2, e2 = parse_verification_output(verif2)
|
|
|
|
if s1 != s2:
|
|
diffs.append(f"Status mismatch: verif1={s1}, verif2={s2}")
|
|
|
|
set1 = set(e1)
|
|
set2 = set(e2)
|
|
only_in_1 = set1 - set2
|
|
only_in_2 = set2 - set1
|
|
|
|
if only_in_1:
|
|
diffs.append(f"Only in verif1 ({len(only_in_1)}):")
|
|
for x in sorted(only_in_1)[:10]:
|
|
diffs.append(f" - {x}")
|
|
if len(only_in_1) > 10:
|
|
diffs.append(f" ... and {len(only_in_1) - 10} more")
|
|
|
|
if only_in_2:
|
|
diffs.append(f"Only in verif2 ({len(only_in_2)}):")
|
|
for x in sorted(only_in_2)[:10]:
|
|
diffs.append(f" - {x}")
|
|
if len(only_in_2) > 10:
|
|
diffs.append(f" ... and {len(only_in_2) - 10} more")
|
|
|
|
return diffs
|
|
|
|
|
|
def run(verif1_path: str, verif2_path: str, output_path: str) -> None:
|
|
"""Compare and write output."""
|
|
diffs = compare(verif1_path, verif2_path)
|
|
|
|
if diffs:
|
|
content = "# Verification comparison: differences\n\n" + "\n".join(diffs)
|
|
else:
|
|
content = "# Verification comparison: identical\n\n"
|
|
|
|
Path(output_path).write_text(content, encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Compare two verification outputs")
|
|
parser.add_argument("--verif1", required=True, help="Path to first verification MD")
|
|
parser.add_argument("--verif2", required=True, help="Path to second verification MD")
|
|
parser.add_argument("--output", required=True, help="Path to output MD file")
|
|
args = parser.parse_args()
|
|
run(args.verif1, args.verif2, args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|