| """Command-line entry point for reviewer-requested reanalysis.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| PROJECT_ROOT = Path(__file__).resolve().parents[2] | |
| if str(PROJECT_ROOT) not in sys.path: | |
| sys.path.insert(0, str(PROJECT_ROOT)) | |
| from revision.scripts.reanalysis_neural import estimate_neural_workload | |
| from revision.scripts.reanalysis_pipeline import run_reanalysis, validate_config | |
| def configure_utf8_console() -> None: | |
| """Make existing Unicode metric labels safe on Windows GBK consoles.""" | |
| for stream in (sys.stdout, sys.stderr): | |
| reconfigure = getattr(stream, "reconfigure", None) | |
| if callable(reconfigure): | |
| reconfigure(encoding="utf-8") | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser( | |
| description=( | |
| "Run canonical-identity-grouped and scaffold-aware reviewer analyses " | |
| "without modifying the original model artifacts." | |
| ) | |
| ) | |
| parser.add_argument( | |
| "--config", | |
| default=str(PROJECT_ROOT / "revision" / "config" / "reanalysis.json"), | |
| help="Path to the frozen JSON configuration.", | |
| ) | |
| parser.add_argument( | |
| "--output-root", | |
| help="Optional new, empty output directory. Existing nonempty directories are refused.", | |
| ) | |
| parser.add_argument( | |
| "--include-neural", | |
| action="store_true", | |
| help="Launch all predeclared GAT/GCN/FPNN fits. This is the long-running stage.", | |
| ) | |
| parser.add_argument( | |
| "--smoke", | |
| action="store_true", | |
| help="Use a small labeled subset, one seed, two folds, and tiny estimators. Never use smoke outputs in the manuscript.", | |
| ) | |
| parser.add_argument( | |
| "--estimate-only", | |
| action="store_true", | |
| help="Validate the configuration and print the neural workload without creating files.", | |
| ) | |
| return parser.parse_args() | |
| def main() -> int: | |
| configure_utf8_console() | |
| args = parse_args() | |
| config_path = Path(args.config).resolve() | |
| config = json.loads(config_path.read_text(encoding="utf-8")) | |
| if args.output_root: | |
| config["output_root"] = args.output_root | |
| normalized = validate_config(config, PROJECT_ROOT) | |
| if args.estimate_only: | |
| print(json.dumps(estimate_neural_workload(normalized), indent=2)) | |
| return 0 | |
| if args.smoke and not args.output_root: | |
| raise ValueError("--smoke requires an explicit new --output-root so smoke artifacts cannot mix with final artifacts.") | |
| output = run_reanalysis( | |
| normalized, | |
| project_root=PROJECT_ROOT, | |
| include_neural=bool(args.include_neural), | |
| smoke_only=bool(args.smoke), | |
| ) | |
| print(f"Reanalysis artifacts written to: {output}") | |
| if not args.include_neural: | |
| print("Neural training was not run. Add --include-neural only after reviewing the split manifests.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |