from __future__ import annotations import argparse import json from datetime import UTC, datetime from pathlib import Path from typing import Any def read_json(path: Path) -> dict[str, Any]: return json.loads(path.read_text(encoding="utf-8")) def rule_status(rule: dict[str, Any]) -> str: confidence = str(rule.get("confidence") or "none") evidence = rule.get("evidence") if isinstance(rule.get("evidence"), dict) else {} ok = evidence.get("ok") total = evidence.get("total") if confidence == "high" and isinstance(ok, int) and isinstance(total, int) and total > 0 and ok == total: return "verified_read" if confidence == "high" and int(evidence.get("distinct_rectangular_samples") or 0) > 0 and int(evidence.get("samples") or 0) > 0: return "verified_read" if confidence in {"medium", "high"}: return "candidate_read" return "needs_more_evidence" def write_status(status: str) -> str: if status == "verified_read": return "blocked_until_roundtrip" return "blocked_until_verified_read" def merge_named_range_analysis_rules(discovery: dict[str, Any], named_range_analysis: dict[str, Any] | None) -> dict[str, Any]: if not named_range_analysis: return discovery merged_rules = [rule for rule in discovery.get("rules") or [] if isinstance(rule, dict)] by_target = {str(rule.get("target") or ""): index for index, rule in enumerate(merged_rules)} for rule in named_range_analysis.get("rules") or []: if not isinstance(rule, dict) or not str(rule.get("target") or "").startswith("moxel.named_range."): continue target = str(rule.get("target") or "") promoted = { "id": rule.get("id") or target, "target": target, "expression": rule.get("expression"), "raw_scalar_indexes": rule.get("raw_scalar_indexes"), "confidence": rule.get("confidence") or "none", "evidence": rule.get("evidence") or {}, "source": "named_range_analysis", } if target in by_target: existing = merged_rules[by_target[target]] confidence_order = {"none": 0, "low": 1, "medium": 2, "high": 3} if confidence_order.get(str(promoted.get("confidence")), 0) >= confidence_order.get(str(existing.get("confidence")), 0): merged_rules[by_target[target]] = {**existing, **promoted} else: by_target[target] = len(merged_rules) merged_rules.append(promoted) return {**discovery, "rules": merged_rules} def build_registry(discovery: dict[str, Any], sources: list[str], named_range_analysis: dict[str, Any] | None = None) -> dict[str, Any]: discovery = merge_named_range_analysis_rules(discovery, named_range_analysis) registry_rules = [] for index, rule in enumerate(discovery.get("rules") or [], start=1): if not isinstance(rule, dict): continue status = rule_status(rule) registry_rules.append( { "id": rule.get("id") or f"moxel_rule_{index}", "target": rule.get("target"), "expression": rule.get("expression"), "raw_scalar_indexes": rule.get("raw_scalar_indexes"), "confidence": rule.get("confidence") or "none", "read_status": status, "write_status": write_status(status), "evidence": rule.get("evidence") or {}, "source_rule": rule, } ) return { "schema": "codex_1c_moxel_schema_registry.v1", "generated_at": datetime.now(UTC).isoformat(timespec="seconds").replace("+00:00", "Z"), "sources": sources, "policy": { "read_use": "Only verified_read rules may be used as decoder behavior without additional diagnostics.", "write_use": "All MOXCEL write rules are blocked until a disposable-base round-trip proves exact behavior.", }, "rules": registry_rules, "counts": { "rules": len(registry_rules), "verified_read": sum(1 for rule in registry_rules if rule.get("read_status") == "verified_read"), "candidate_read": sum(1 for rule in registry_rules if rule.get("read_status") == "candidate_read"), "write_enabled": sum(1 for rule in registry_rules if rule.get("write_status") == "verified_roundtrip"), }, } def render_markdown(registry: dict[str, Any]) -> str: lines = ["# 1C MOXCEL Schema Registry", ""] counts = registry.get("counts") or {} lines.append(f"- Rules: `{counts.get('rules')}`") lines.append(f"- Verified read: `{counts.get('verified_read')}`") lines.append(f"- Candidate read: `{counts.get('candidate_read')}`") lines.append(f"- Write enabled: `{counts.get('write_enabled')}`") lines.append("") lines.append("| Rule | Target | Read | Write | Confidence |") lines.append("| --- | --- | --- | --- | --- |") for rule in registry.get("rules") or []: lines.append( f"| `{rule.get('id')}` | `{rule.get('target')}` | `{rule.get('read_status')}` | " f"`{rule.get('write_status')}` | `{rule.get('confidence')}` |" ) lines.append("") return "\n".join(lines) def main() -> int: parser = argparse.ArgumentParser(description="Build the stable 1C MOXCEL schema registry from discovery reports.") parser.add_argument("--discovery", action="append", required=True, help="Discovery JSON. Repeatable; rules are merged in order.") parser.add_argument("--named-range-analysis", help="Optional named range rule analysis JSON.") parser.add_argument("--output-json", default="plugins/1c/metadata/moxel-schema-registry.json") parser.add_argument("--output-markdown", default="reports/1c-template-baselines/moxel-schema-registry.md") args = parser.parse_args() discoveries = [read_json(Path(path)) for path in args.discovery] merged = {"rules": []} seen: set[tuple[str, str]] = set() for discovery in discoveries: for rule in discovery.get("rules") or []: if not isinstance(rule, dict): continue key = (str(rule.get("id") or ""), str(rule.get("target") or "")) if key in seen: continue seen.add(key) merged["rules"].append(rule) named_range_analysis = read_json(Path(args.named_range_analysis)) if args.named_range_analysis else None sources = list(args.discovery) if args.named_range_analysis: sources.append(args.named_range_analysis) registry = build_registry(merged, sources, named_range_analysis) json_path = Path(args.output_json) md_path = Path(args.output_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(registry, ensure_ascii=False, indent=2), encoding="utf-8") md_path.write_text(render_markdown(registry), encoding="utf-8") print(json.dumps({"status": "ok", "json": str(json_path), "markdown": str(md_path), "counts": registry["counts"]}, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())