from __future__ import annotations import argparse import json import subprocess import sys from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[1] DEFAULT_PROBE = ROOT / "reports" / "1c-template-probes" / "upo_test_auto_20260627T142419Z_670780b4.json" DEFAULT_BASELINE_DISCOVERY = ROOT / "reports" / "1c-template-baselines" / "Primer3_moxel_schema_discovery.json" DEFAULT_HISTORY_MATRIX = ROOT / "reports" / "1c-template-baselines" / "Primer3_history_matrix.json" DEFAULT_PROPERTY_CANDIDATES = ROOT / "reports" / "1c-template-baselines" / "Primer3_property_candidates.json" DEFAULT_PROPERTY_MANIFEST = ROOT / "reports" / "1c-template-baselines" / "Primer3_moxel_property_experiments.manifest.json" def rel(path: Path) -> str: try: return str(path.resolve().relative_to(ROOT)) except ValueError: return str(path) def run_step(name: str, command: list[str], *, dry_run: bool = False) -> dict[str, Any]: if dry_run: return {"name": name, "command": command, "status": "planned", "returncode": None, "stdout": "", "stderr": ""} result = subprocess.run(command, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) return { "name": name, "command": command, "status": "ok" if result.returncode == 0 else "failed", "returncode": result.returncode, "stdout": result.stdout.strip(), "stderr": result.stderr.strip(), } def build_commands(args: argparse.Namespace) -> list[tuple[str, list[str]]]: py = sys.executable probe_path = Path(args.probe).resolve() live_discovery_json = ROOT / "reports" / "1c-template-probes" / "upo_test_auto_moxel_schema_discovery.json" live_discovery_md = ROOT / "reports" / "1c-template-probes" / "upo_test_auto_moxel_schema_discovery.md" property_json = ROOT / "reports" / "1c-template-baselines" / "Primer3_moxel_property_experiments.json" property_md = ROOT / "reports" / "1c-template-baselines" / "Primer3_moxel_property_experiments.md" named_range_json = ROOT / "reports" / "1c-template-baselines" / "moxel-named-range-rules.json" named_range_md = ROOT / "reports" / "1c-template-baselines" / "moxel-named-range-rules.md" registry_json = ROOT / "plugins" / "1c" / "metadata" / "moxel-schema-registry.json" registry_md = ROOT / "reports" / "1c-template-baselines" / "moxel-schema-registry.md" registry_check = ROOT / "reports" / "1c-template-baselines" / "moxel-schema-registry-check.json" registry_verification = ROOT / "reports" / "1c-template-baselines" / "moxel-schema-registry-verification.json" next_experiments_json = ROOT / "reports" / "1c-template-baselines" / "moxel-next-experiments.json" next_experiments_md = ROOT / "reports" / "1c-template-baselines" / "moxel-next-experiments.md" next_action_json = ROOT / "reports" / "1c-template-baselines" / "moxel-next-action.json" next_action_md = ROOT / "reports" / "1c-template-baselines" / "moxel-next-action.md" next_action_check = ROOT / "reports" / "1c-template-baselines" / "moxel-next-action-check.json" commands: list[tuple[str, list[str]]] = [] if args.capture_live: probe_path = ROOT / "reports" / "1c-template-probes" / "latest-live-probe.json" probe_markdown = ROOT / "reports" / "1c-template-probes" / "latest-live-probe.md" capture_command = [ py, "scripts/capture_1c_template_probe.py", "--base-id", args.base_id, "--label", args.label, "--output-dir", "reports/1c-template-probes", "--output-json", rel(probe_path), "--output-markdown", rel(probe_markdown), "--scan-limit", str(args.scan_limit), "--max-cells", str(args.max_cells), ] if args.file_name: capture_command.extend(["--file-name", args.file_name]) commands.append( ( "capture_live_probe", capture_command, ) ) commands.extend( [ ( "discover_schema", [ py, "scripts/discover_1c_moxel_schema.py", "--probe-snapshot", rel(probe_path), "--history-matrix", rel(Path(args.history_matrix)), "--property-candidates", rel(Path(args.property_candidates)), "--output-json", rel(live_discovery_json), "--output-markdown", rel(live_discovery_md), ], ), ( "analyze_named_range_rules", [ py, "scripts/analyze_1c_moxel_named_range_rules.py", "--probe", rel(probe_path), "--target-name", "R7C2_TEST", "--output-json", rel(named_range_json), "--output-markdown", rel(named_range_md), ], ), ( "analyze_property_experiments", [ py, "scripts/analyze_1c_moxel_property_experiments.py", "--manifest", rel(Path(args.property_manifest)), "--emit-default-plan", "--output-json", rel(property_json), "--output-markdown", rel(property_md), ], ), ( "build_registry", [ py, "scripts/build_1c_moxel_schema_registry.py", "--discovery", rel(live_discovery_json), "--discovery", rel(Path(args.baseline_discovery)), "--named-range-analysis", rel(named_range_json), "--output-json", rel(registry_json), "--output-markdown", rel(registry_md), ], ), ( "check_registry", [ py, "scripts/check_1c_moxel_schema_registry.py", "--registry", rel(registry_json), "--output", rel(registry_check), ], ), ( "verify_registry", [ py, "scripts/verify_1c_moxel_schema_registry.py", "--registry", rel(registry_json), "--probe", rel(probe_path), "--output", rel(registry_verification), ], ), ( "plan_next_experiments", [ py, "scripts/plan_1c_moxel_next_experiments.py", "--registry", rel(registry_json), "--verification", rel(registry_verification), "--output-json", rel(next_experiments_json), "--output-markdown", rel(next_experiments_md), ], ), ( "get_next_action", [ py, "scripts/get_1c_moxel_next_action.py", "--plan", rel(next_experiments_json), "--output-json", rel(next_action_json), "--output-markdown", rel(next_action_md), ], ), ( "check_next_action", [ py, "scripts/check_1c_moxel_next_action.py", "--plan", rel(next_experiments_json), "--action", rel(next_action_json), "--output", rel(next_action_check), ], ), ( "status", [ py, "scripts/status_1c_moxel.py", ], ), ] ) return commands def render_markdown(report: dict[str, Any]) -> str: lines = ["# 1C MOXCEL Discovery Pipeline", ""] lines.append(f"- Status: `{report.get('status')}`") lines.append(f"- Steps: `{len(report.get('steps') or [])}`") lines.append("") lines.append("| Step | Status |") lines.append("| --- | --- |") for step in report.get("steps") or []: lines.append(f"| `{step.get('name')}` | `{step.get('status')}` |") lines.append("") return "\n".join(lines) def main() -> int: parser = argparse.ArgumentParser(description="Run the complete 1C MOXCEL discovery and registry pipeline.") parser.add_argument("--probe", default=str(DEFAULT_PROBE), help="Existing probe snapshot JSON.") parser.add_argument("--baseline-discovery", default=str(DEFAULT_BASELINE_DISCOVERY)) parser.add_argument("--history-matrix", default=str(DEFAULT_HISTORY_MATRIX)) parser.add_argument("--property-candidates", default=str(DEFAULT_PROPERTY_CANDIDATES)) parser.add_argument("--property-manifest", default=str(DEFAULT_PROPERTY_MANIFEST)) parser.add_argument("--capture-live", action="store_true", help="Capture a live probe before running the pipeline.") parser.add_argument("--base-id", default="upo_test") parser.add_argument("--label", default="pipeline") parser.add_argument("--file-name", help="Explicit ConfigCAS file name for live capture.") parser.add_argument("--scan-limit", type=int, default=30) parser.add_argument("--max-cells", type=int, default=800) parser.add_argument("--dry-run", action="store_true") parser.add_argument("--report-json", default="reports/1c-template-baselines/moxel-discovery-pipeline.json") parser.add_argument("--report-markdown", default="reports/1c-template-baselines/moxel-discovery-pipeline.md") args = parser.parse_args() steps = [] failures = [] for name, command in build_commands(args): step = run_step(name, command, dry_run=args.dry_run) steps.append(step) if step["status"] == "failed": failures.append(name) break report = { "schema": "codex_1c_moxel_discovery_pipeline.v1", "status": "failed" if failures else "planned" if args.dry_run else "ok", "failed_steps": failures, "steps": steps, } json_path = ROOT / args.report_json md_path = ROOT / args.report_markdown json_path.parent.mkdir(parents=True, exist_ok=True) md_path.parent.mkdir(parents=True, exist_ok=True) json_path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") md_path.write_text(render_markdown(report), encoding="utf-8") print(json.dumps({"status": report["status"], "json": rel(json_path), "markdown": rel(md_path), "failed_steps": failures}, ensure_ascii=False, indent=2)) return 1 if failures else 0 if __name__ == "__main__": raise SystemExit(main())