diff --git a/.github/scripts/generate_rationales.py b/.github/scripts/generate_rationales.py new file mode 100644 index 000000000..2ca66f0b2 --- /dev/null +++ b/.github/scripts/generate_rationales.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Reads summary_table.md and writes rationales.csv. + +The output CSV contains one row per failed test case (Match = ❌) with blank +Fixed and Reason columns, ready to be filled in by reviewer. + +Usage: + python generate_rationales.py +""" + +import argparse +import csv +import re +import sys +from pathlib import Path + + +def generate(summary_path: Path, output_path: Path) -> int: + if not summary_path.exists(): + print(f"ERROR: {summary_path} not found", file=sys.stderr) + return 1 + + failed = [] + for line in summary_path.read_text(encoding="utf-8").splitlines(): + if not line.startswith("|"): + continue + cols = [c.strip() for c in line.strip("|").split("|")] + if len(cols) < 6: + continue + rule, typ, num, match = cols[0], cols[1], cols[2], cols[5] + if not re.match(r"CORE-\d+", rule): + continue + if "\u274c" in match: # ❌ + failed.append((rule, typ, num)) + + with output_path.open("w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["Rule", "Type", "Number", "Fixed", "Reason"]) + for rule, typ, num in failed: + writer.writerow([rule, typ, num, "", ""]) + + print(f"Wrote {len(failed)} rationales to {output_path}") + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Generate a rationales.csv template from summary_table.md." + ) + parser.add_argument("summary", type=Path, help="Path to summary_table.md") + parser.add_argument("output", type=Path, help="Path for the output rationales.csv") + args = parser.parse_args() + sys.exit(generate(args.summary, args.output)) diff --git a/.github/scripts/run_validation.sh b/.github/scripts/run_validation.sh index 091fa2d32..8c562b5ff 100644 --- a/.github/scripts/run_validation.sh +++ b/.github/scripts/run_validation.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# run_validation.sh — iterates all positive/ and negative/ test cases for a rule, +# run_validation.sh — iterates all negative/ and positive/ test cases for a rule, # runs the CORE engine against each, prints output to results.csv, # diffs against any expected results.csv, and writes two outputs: # - $REPO_ROOT/validation_report.md (detailed markdown, legacy/fallback) @@ -86,7 +86,7 @@ print(json.dumps({ # --------------------------------------------------------------------------- # Iterate test types and cases # --------------------------------------------------------------------------- -for TEST_TYPE in positive negative; do +for TEST_TYPE in negative positive; do TYPE_DIR="$RULE_DIR/$TEST_TYPE" [ -d "$TYPE_DIR" ] || continue diff --git a/.github/workflows/update-published-results.yml b/.github/workflows/update-published-results.yml index 1912c9596..498dd9342 100644 --- a/.github/workflows/update-published-results.yml +++ b/.github/workflows/update-published-results.yml @@ -122,6 +122,13 @@ jobs: run: | [ -f summary_table.md ] && cat summary_table.md >> $GITHUB_STEP_SUMMARY || true + # ----------------------------------------------------------------------- + # 6a. Generate rationales.csv template from summary_table.md + # ----------------------------------------------------------------------- + - name: Generate rationales CSV + if: always() + run: python .github/scripts/generate_rationales.py summary_table.md rationales.csv + # ----------------------------------------------------------------------- # 7. Upload reports as artifacts (regardless of outcome) # ----------------------------------------------------------------------- @@ -134,6 +141,7 @@ jobs: Published/**/results/results.csv summary_table.md detail_report.md + rationales.csv if-no-files-found: warn # -----------------------------------------------------------------------