diff --git a/README.md b/README.md index 7e88a34a..130931e2 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,14 @@ python3 scripts/oracle_boundary_audit.py \ 该报告会明确标出 `production_tuning_recommended: false`:Dasha 当前只有单份 PDF 起点差异样本;VedAstro SDK 黄经样本已纳入 `longitude_cases`,当前用户盘最大差异约 26.23 角秒、D1/D9 落点一致,但这只能说明基础黄经接近;Shadbala 还缺 Sthana/Dig/Kala/Chesta/Naisargika/Drik 分量级外部目标值,因此不能声称 Dasha/Shadbala 已完成外部绝对值校准。 +三方官方/外部 oracle sanity 总控命令: + +```bash +python3 scripts/external_oracle_sanity_closure.py --format markdown +``` + +该报告同时检查 VedAstro official precision sanity、PyJHora black-box artifacts、jyotishganit MIT reference layer。默认命令为非阻塞 sanity,总控质量门可运行;需要真实触发 VedAstro official full snapshot 细算时,加 `--live-official-full-snapshot`。当前允许声明的是“高严谨带 blocked 行”:PyJHora 黑盒证据与 jyotishganit 参考层可审计;VedAstro 黄经 sanity 通过但 official full snapshot 细算仍需显式 live probe 且可能因前台预算或官方快照合同 blocked,不能声称三方 fully closed。 + 外部真值采集队列用于把缺失目标值拆成可执行任务,而不是直接调生产参数: ```bash diff --git a/docs/benchmark/external_official_sanity_oracle_closure.json b/docs/benchmark/external_official_sanity_oracle_closure.json new file mode 100644 index 00000000..b0315629 --- /dev/null +++ b/docs/benchmark/external_official_sanity_oracle_closure.json @@ -0,0 +1,217 @@ +{ + "scope": "external_official_sanity_oracle_closure", + "schema_version": 1, + "generated_at": "2026-07-01T12:03:22.602473+00:00", + "summary": { + "required_oracles": [ + "VedAstro", + "PyJHora", + "jyotishganit" + ], + "ok_count": 2, + "partial_count": 0, + "blocked_count": 1, + "ok_oracles": [ + "pyjhora", + "jyotishganit" + ], + "partial_oracles": [], + "blocked_oracles": [ + "vedastro" + ], + "external_verified_tasks": 12, + "total_tasks": 12, + "open_tasks": 0, + "live_official_full_snapshot": false + }, + "oracle_ledger": { + "vedastro": { + "name": "VedAstro", + "role": "official_precision_sanity", + "status": "blocked", + "verdict": "official_longitude_sanity_passed_but_full_snapshot_blocked", + "live_official_full_snapshot": false, + "fine_calc_blocked": true, + "longitude_case_count": 1, + "longitude_cases_within_threshold": 1, + "max_abs_delta_arcsec": 26.2254, + "snapshot_status": "not_run_default_non_blocking", + "official_chart_available": false, + "official_python_bundle_status": "not_run_default_non_blocking", + "evidence_paths": [ + "references/oracle/dasha_shadbala_oracle_cases.json", + "scripts/oracle_boundary_audit.py", + "scripts/vedastro_service_adapter.py" + ], + "blocked_reason": "Official fine snapshot is not a stable ok chart, or only longitude sanity is currently closed." + }, + "pyjhora": { + "name": "PyJHora", + "role": "black_box_external_oracle", + "status": "ok", + "verdict": "black_box_artifact_ledger_available", + "artifact_count": 12, + "packet_count": 8, + "fronts": { + "dasha": { + "artifact_count": 3, + "packet_count": 3, + "artifacts": [ + "pyjhora_historical_epoch_dasha_stdout_20260627.txt", + "pyjhora_steve_jobs_dasha_stdout_20260627.txt", + "pyjhora_user_REDACTED_YEAR_dasha_stdout_20260627.txt" + ], + "pending_packets": [ + "external_template_extreme_latitude_kp_pyjhora_20260627.json", + "external_template_historical_epoch_lahiri_pyjhora_20260627.json", + "external_template_steve_jobs_dasha_lahiri_pyjhora_20260627.json" + ] + }, + "shadbala": { + "artifact_count": 4, + "packet_count": 4, + "artifacts": [ + "pyjhora_extreme_latitude_kp_shadbala_stdout_20260627.txt", + "pyjhora_redacted_place_shadbala_raman_stdout_20260627.txt", + "pyjhora_steve_jobs_shadbala_lahiri_stdout_20260627.txt", + "pyjhora_user_REDACTED_YEAR_shadbala_lahiri_stdout_20260627.txt" + ], + "pending_packets": [ + "external_template_redacted_place_shadbala_raman_pyjhora_20260627.json", + "external_template_steve_jobs_shadbala_lahiri_pyjhora_20260627.json", + "external_template_user_REDACTED_YEAR_moon_longitude_lahiri_pyjhora_20260627.json", + "external_template_user_REDACTED_YEAR_shadbala_lahiri_pyjhora_20260627.json" + ] + }, + "tajika_sahams": { + "artifact_count": 5, + "packet_count": 1, + "artifacts": [ + "pyjhora_einstein_varshaphala_1905_lahiri_partial_20260629.txt", + "pyjhora_historical_dst_london_varshaphala_1943_lahiri_20260629.txt", + "pyjhora_marilyn_monroe_varshaphala_1962_lahiri_20260629.txt", + "pyjhora_steve_jobs_varshaphala_1984_lahiri_stdout_20260627.txt", + "pyjhora_synthetic_extreme_latitude_varshaphala_kp_20260629.txt" + ], + "pending_packets": [ + "external_template_steve_jobs_varshaphala_1984_lahiri_pyjhora_20260627.json" + ] + } + }, + "license_boundary": "black_box_artifacts_only_no_agpl_code_import", + "evidence_paths": [ + "references/oracle/artifacts/pyjhora_oracle_artifact_manifest.json" + ] + }, + "jyotishganit": { + "name": "jyotishganit", + "role": "mit_reference_layer", + "status": "ok", + "verdict": "mit_reference_source_available", + "license": "MIT", + "source_available": true, + "import_spec_available": true, + "import_error": null, + "benchmark_available": true, + "evidence_paths": [ + "references/open_source_sources/jyotishganit", + "references/jyotishganit_benchmark.md" + ], + "boundary": "MIT reference layer and local comparison asset; not a complete second runtime oracle for every request." + } + }, + "master_oracle_dashboard": { + "summary": { + "total_tasks": 12, + "external_verified_tasks": 12, + "open_tasks": 0, + "can_claim_global_oracle_closure": true, + "production_tuning_allowed": false + }, + "fronts": { + "dasha": { + "front": "dasha", + "task_count": 3, + "external_verified_tasks": 3, + "open_tasks": 0, + "can_claim_closure": true, + "production_tuning_allowed": false, + "first_priority": null + }, + "shadbala": { + "front": "shadbala", + "task_count": 4, + "external_verified_tasks": 4, + "open_tasks": 0, + "can_claim_closure": true, + "production_tuning_allowed": false, + "first_priority": null + }, + "tajika_sahams": { + "front": "tajika_sahams", + "task_count": 5, + "external_verified_tasks": 5, + "open_tasks": 0, + "can_claim_closure": true, + "production_tuning_allowed": false, + "first_priority": { + "case_id": "template_einstein_varshaphala_1905_lahiri", + "capture_id": "external_template_einstein_varshaphala_1905_lahiri", + "packet_path": "references/oracle/artifacts/pending_packets/external_template_einstein_varshaphala_1905_lahiri.json", + "missing_field_count": 0, + "missing_fields": [], + "missing_groups": { + "metadata": { + "count": 0, + "fields": [] + }, + "target": { + "count": 0, + "fields": [] + } + }, + "prefilled_fields": { + "status": "external_verified", + "promotion_status_after_fill": "external_verified", + "metadata": { + "tool_name": "PyJHora", + "tool_version_or_url": "PyJHora 4.8.6 isolated workbuddy black-box run", + "capture_date": "2026-06-29", + "source_artifact": "references/oracle/artifacts/pyjhora_einstein_varshaphala_1905_lahiri_partial_20260629.txt", + "ayanamsa": "lahiri", + "node_mode": "mean", + "timezone": "UTC+00:53", + "annual_system": "Varshaphala/Tajika", + "target_year": 1905, + "operator_note": "Black-box annual output from workbuddy PyJHora 4.8.6 environment. External evidence only; local annual engine output was not used. Solar-return timestamp, Varsha Lagna, Muntha, Year Lord, first Mudda Dasha lord, selected Sahams, and Tajika Yogas were captured through the external PyJHora path via the annual chart and jhora.horoscope.transit.tajaka_yoga helper chain." + }, + "settings": { + "ayanamsa": "lahiri", + "node_mode": "mean", + "annual_system": "varshaphala", + "target_year": 1905 + } + }, + "manual_fill_plan": { + "status_value": "external_verified", + "manual_entry_count": 0, + "remaining_manual_fields": [] + }, + "apply_command": "python3 scripts/tajika_annual_oracle_queue.py --oracle-file references/oracle/tajika_annual_oracle_cases.json --apply-packet references/oracle/artifacts/pending_packets/external_template_einstein_varshaphala_1905_lahiri.json --format json", + "validate_command": "python3 scripts/tajika_annual_oracle_queue.py --oracle-file references/oracle/tajika_annual_oracle_cases.json --format json && python3 scripts/tajika_annual_benchmark_dashboard.py --oracle-file references/oracle/tajika_annual_oracle_cases.json --format json" + } + } + } + }, + "honesty_boundary": { + "can_claim_fully_closed": false, + "can_claim_high_rigor_with_blocks": true, + "blocked_reason": "At least one official precision/oracle layer is partial or blocked; report must expose blocked rows instead of claiming full closure.", + "license_boundary": "PyJHora is black-box evidence only; jyotishganit MIT assets may be referenced/reused within existing project policy." + }, + "next_actions": [ + "Run --live-official-full-snapshot when foreground VedAstro budget/network credentials are available, then promote VedAstro only if the full official chart is stable.", + "Keep PyJHora as artifact-backed oracle evidence; do not import AGPL implementation code.", + "If a report needs full closure language, require all oracle ledger rows to be ok and the master oracle dashboard open_tasks to be 0." + ] +} \ No newline at end of file diff --git a/docs/benchmark/external_official_sanity_oracle_closure.md b/docs/benchmark/external_official_sanity_oracle_closure.md new file mode 100644 index 00000000..4aaa32ca --- /dev/null +++ b/docs/benchmark/external_official_sanity_oracle_closure.md @@ -0,0 +1,32 @@ +# External Official Sanity / Oracle Closure + +Generated: `2026-07-01T12:03:21.916094+00:00` + +## Summary + +- ok_count: `2` +- partial_count: `0` +- blocked_count: `1` +- total_tasks: `12` +- external_verified_tasks: `12` +- open_tasks: `0` + +## Oracle Ledger + +| oracle | status | role | verdict | +|---|---|---|---| +| `vedastro` | `blocked` | `official_precision_sanity` | `official_longitude_sanity_passed_but_full_snapshot_blocked` | +| `pyjhora` | `ok` | `black_box_external_oracle` | `black_box_artifact_ledger_available` | +| `jyotishganit` | `ok` | `mit_reference_layer` | `mit_reference_source_available` | + +## Honesty Boundary + +- can_claim_fully_closed: `false` +- can_claim_high_rigor_with_blocks: `true` +- blocked_reason: At least one official precision/oracle layer is partial or blocked; report must expose blocked rows instead of claiming full closure. + +## Next Actions + +- Run --live-official-full-snapshot when foreground VedAstro budget/network credentials are available, then promote VedAstro only if the full official chart is stable. +- Keep PyJHora as artifact-backed oracle evidence; do not import AGPL implementation code. +- If a report needs full closure language, require all oracle ledger rows to be ok and the master oracle dashboard open_tasks to be 0. diff --git a/progress.md b/progress.md index d0f939f3..d3d2aaba 100644 --- a/progress.md +++ b/progress.md @@ -708,3 +708,11 @@ - `scripts/guided_topic_discovery.py` 已把 `official_day_signal_summary` 压进每条 guided topic;`jyotish-app/main.js` 的 guided topic 卡片会直接显示最重要的官方日期提示;`jyotish-app/ai-chat.js` 也会把这层塞进 guided topic chat payload,后续追问不再丢失。 - 新增并跑通的定点回归: - `python3 -m pytest tests/test_mcp_strict_workflow_career.py::test_career_external_activation_derives_user_readable_day_signals tests/test_mcp_strict_workflow_relationship.py::test_relationship_external_activation_derives_progress_day_signals tests/test_mcp_strict_workflow_finance.py::test_finance_external_activation_derives_wealth_day_signals tests/test_cli_smoke.py::test_full_reading_guided_topics_can_carry_official_day_signal_summary tests/test_frontend_productization.py::test_guided_topic_questions_reuse_ai_chat_entry -q` + +## 2026-07-01T19:40:00+08:00 - 外部官方细算 sanity / 三方 oracle 闭环账本 + +- 新增 `scripts/external_oracle_sanity_closure.py`:把 VedAstro official precision sanity、PyJHora black-box artifact ledger、jyotishganit MIT reference layer 统一成可审计 JSON/Markdown 账本。 +- 当前三方状态:PyJHora `ok`(12 artifacts / 8 packets)、jyotishganit `ok`(MIT source + benchmark available)、VedAstro `blocked`(longitude sanity 通过,但 official full snapshot 细算仍返回 `official_snapshot_budget_exhausted`,不能宣称 fully closed)。 +- 新增 `tests/test_external_oracle_sanity_closure.py`,并将 sanity closure 纳入 `scripts/run_quality_gate.py` 的 oracle audit 链路;README 增加总控命令。 +- 生成 `docs/benchmark/external_official_sanity_oracle_closure.{md,json}` 作为当前快照;诚实边界为 `can_claim_fully_closed=false`、`can_claim_high_rigor_with_blocks=true`。 +- 为避免质量门被 VedAstro 免费层节流长时间阻塞,sanity closure 默认只跑非阻塞官方证据审计;真实 official full snapshot 细算保留为显式 `--live-official-full-snapshot` 开关。 diff --git a/scripts/external_oracle_sanity_closure.py b/scripts/external_oracle_sanity_closure.py new file mode 100644 index 00000000..754bf6f4 --- /dev/null +++ b/scripts/external_oracle_sanity_closure.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +"""Summarize VedAstro/PyJHora/jyotishganit sanity closure status. + +This is an audit ledger, not a calculator. It cross-references existing +external evidence and reports which parts are closed, partial, or blocked +without importing PyJHora AGPL code or treating local consistency as truth. +""" + +from __future__ import annotations + +import argparse +import importlib.util +import json +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +PYTHON = sys.executable +DEFAULT_DASHA_ORACLE_FILE = "references/oracle/dasha_shadbala_oracle_cases.json" +DEFAULT_TAJIKA_ORACLE_FILE = "references/oracle/tajika_annual_oracle_cases.json" +JYOTISHGANIT_ROOT = ROOT / "references" / "open_source_sources" / "jyotishganit" + + +def _run_json(command: list[str], *, timeout: int = 120) -> dict[str, Any]: + completed = subprocess.run( + command, + cwd=ROOT, + text=True, + capture_output=True, + timeout=timeout, + check=False, + ) + if completed.returncode != 0: + return { + "status": "blocked", + "command": command, + "stderr": completed.stderr.strip(), + "stdout_excerpt": completed.stdout.strip()[:500], + } + try: + data = json.loads(completed.stdout) + except json.JSONDecodeError as exc: + return { + "status": "blocked", + "command": command, + "reason": f"invalid_json: {exc}", + "stdout_excerpt": completed.stdout.strip()[:500], + } + if isinstance(data, dict): + return data + return {"status": "blocked", "command": command, "reason": "json_root_not_object"} + + +def _load_json(path: Path) -> dict[str, Any]: + try: + data = json.loads(path.read_text(encoding="utf-8")) + except Exception as exc: # pragma: no cover - exact filesystem errors vary + return {"status": "blocked", "reason": f"{type(exc).__name__}: {exc}"} + return data if isinstance(data, dict) else {"status": "blocked", "reason": "json_root_not_object"} + + +def _relative(path: Path) -> str: + try: + return str(path.relative_to(ROOT)) + except ValueError: + return str(path) + + +def _vedastro_ledger(oracle_file: str, *, live_official_full_snapshot: bool = False) -> dict[str, Any]: + boundary = _run_json([PYTHON, "scripts/oracle_boundary_audit.py", "--oracle-file", oracle_file]) + longitude_cases = boundary.get("longitude_cases") if isinstance(boundary.get("longitude_cases"), list) else [] + within_threshold = [ + row for row in longitude_cases + if isinstance(row, dict) and row.get("within_threshold") is True + ] + max_delta = max( + (float(row.get("max_abs_delta_arcsec", 0.0)) for row in longitude_cases if isinstance(row, dict)), + default=None, + ) + boundary_status = "ok" if longitude_cases and len(within_threshold) == len(longitude_cases) else "blocked" + + if live_official_full_snapshot: + snapshot = _run_json([ + PYTHON, + "scripts/vedastro_service_adapter.py", + "--official-full-snapshot", + "--case", + "user_REDACTED_YEAR_test", + ]) + else: + snapshot = { + "status": "not_run_default_non_blocking", + "source_metadata": { + "official_python_bundle": { + "status": "not_run_default_non_blocking", + }, + }, + "official_chart": {}, + } + snapshot_status = str(snapshot.get("status") or "blocked") + source_metadata = snapshot.get("source_metadata") if isinstance(snapshot.get("source_metadata"), dict) else {} + official_bundle = source_metadata.get("official_python_bundle") if isinstance(source_metadata.get("official_python_bundle"), dict) else {} + official_chart = snapshot.get("official_chart") if isinstance(snapshot.get("official_chart"), dict) else {} + official_chart_available = bool( + isinstance(official_chart.get("planets"), dict) + and official_chart.get("planets") + and isinstance(official_chart.get("ascendant"), dict) + and official_chart.get("ascendant") + ) + + fine_status_ok = snapshot_status in {"ok", "partial"} and official_chart_available + if fine_status_ok and boundary_status == "ok": + status = "ok" + verdict = "official_precision_sanity_passed" + else: + status = "blocked" + verdict = ( + "official_longitude_sanity_passed_but_full_snapshot_blocked" + if boundary_status == "ok" + else "official_precision_sanity_blocked" + ) + + return { + "name": "VedAstro", + "role": "official_precision_sanity", + "status": status, + "verdict": verdict, + "live_official_full_snapshot": live_official_full_snapshot, + "fine_calc_blocked": status != "ok", + "longitude_case_count": len(longitude_cases), + "longitude_cases_within_threshold": len(within_threshold), + "max_abs_delta_arcsec": max_delta, + "snapshot_status": snapshot_status, + "official_chart_available": official_chart_available, + "official_python_bundle_status": official_bundle.get("status"), + "evidence_paths": [ + "references/oracle/dasha_shadbala_oracle_cases.json", + "scripts/oracle_boundary_audit.py", + "scripts/vedastro_service_adapter.py", + ], + "blocked_reason": None if status == "ok" else ( + "Official fine snapshot is not a stable ok chart, or only longitude sanity is currently closed." + ), + } + + +def _pyjhora_ledger() -> dict[str, Any]: + manifest_path = ROOT / "references" / "oracle" / "artifacts" / "pyjhora_oracle_artifact_manifest.json" + manifest = _load_json(manifest_path) + artifact_count = int(manifest.get("artifact_count") or 0) + packet_count = int(manifest.get("packet_count") or 0) + fronts = manifest.get("fronts") if isinstance(manifest.get("fronts"), dict) else {} + status = "ok" if artifact_count >= 8 and packet_count >= 8 else "partial" if artifact_count else "blocked" + return { + "name": "PyJHora", + "role": "black_box_external_oracle", + "status": status, + "verdict": "black_box_artifact_ledger_available" if status != "blocked" else "black_box_artifact_ledger_missing", + "artifact_count": artifact_count, + "packet_count": packet_count, + "fronts": fronts, + "license_boundary": "black_box_artifacts_only_no_agpl_code_import", + "evidence_paths": [_relative(manifest_path)], + } + + +def _jyotishganit_ledger() -> dict[str, Any]: + package_init = JYOTISHGANIT_ROOT / "jyotishganit" / "__init__.py" + license_path = JYOTISHGANIT_ROOT / "LICENSE" + source_available = package_init.is_file() + license_text = "" + if license_path.is_file(): + license_text = license_path.read_text(encoding="utf-8", errors="ignore")[:2000].lower() + license_name = "MIT" if "mit license" in license_text or "permission is hereby granted" in license_text else "unknown" + + importable = False + import_error = None + if source_available: + spec = importlib.util.spec_from_file_location("jyotishganit", package_init) + importable = spec is not None + if not importable: + import_error = "cannot_build_import_spec" + + benchmark_path = ROOT / "references" / "jyotishganit_benchmark.md" + status = "ok" if source_available and license_name == "MIT" and benchmark_path.is_file() else "partial" if source_available else "blocked" + return { + "name": "jyotishganit", + "role": "mit_reference_layer", + "status": status, + "verdict": "mit_reference_source_available" if status != "blocked" else "mit_reference_source_missing", + "license": license_name, + "source_available": source_available, + "import_spec_available": importable, + "import_error": import_error, + "benchmark_available": benchmark_path.is_file(), + "evidence_paths": [ + _relative(JYOTISHGANIT_ROOT), + _relative(benchmark_path), + ], + "boundary": "MIT reference layer and local comparison asset; not a complete second runtime oracle for every request.", + } + + +def _oracle_master(dasha_oracle_file: str, tajika_oracle_file: str) -> dict[str, Any]: + return _run_json([ + PYTHON, + "scripts/oracle_closure_master_dashboard.py", + "--dasha-oracle-file", + dasha_oracle_file, + "--tajika-oracle-file", + tajika_oracle_file, + "--format", + "json", + ]) + + +def build_report( + dasha_oracle_file: str, + tajika_oracle_file: str, + *, + live_official_full_snapshot: bool = False, +) -> dict[str, Any]: + vedastro = _vedastro_ledger( + dasha_oracle_file, + live_official_full_snapshot=live_official_full_snapshot, + ) + pyjhora = _pyjhora_ledger() + jyotishganit = _jyotishganit_ledger() + master = _oracle_master(dasha_oracle_file, tajika_oracle_file) + + ledgers = { + "vedastro": vedastro, + "pyjhora": pyjhora, + "jyotishganit": jyotishganit, + } + blocked = [key for key, value in ledgers.items() if value.get("status") == "blocked"] + partial = [key for key, value in ledgers.items() if value.get("status") == "partial"] + ok = [key for key, value in ledgers.items() if value.get("status") == "ok"] + can_claim_fully_closed = not blocked and not partial and master.get("summary", {}).get("can_claim_global_oracle_closure") is True + + return { + "scope": "external_official_sanity_oracle_closure", + "schema_version": 1, + "generated_at": datetime.now(timezone.utc).isoformat(), + "summary": { + "required_oracles": ["VedAstro", "PyJHora", "jyotishganit"], + "ok_count": len(ok), + "partial_count": len(partial), + "blocked_count": len(blocked), + "ok_oracles": ok, + "partial_oracles": partial, + "blocked_oracles": blocked, + "external_verified_tasks": master.get("summary", {}).get("external_verified_tasks"), + "total_tasks": master.get("summary", {}).get("total_tasks"), + "open_tasks": master.get("summary", {}).get("open_tasks"), + "live_official_full_snapshot": live_official_full_snapshot, + }, + "oracle_ledger": ledgers, + "master_oracle_dashboard": { + "summary": master.get("summary", {}), + "fronts": master.get("fronts", {}), + }, + "honesty_boundary": { + "can_claim_fully_closed": can_claim_fully_closed, + "can_claim_high_rigor_with_blocks": bool(pyjhora.get("status") in {"ok", "partial"} and jyotishganit.get("status") in {"ok", "partial"}), + "blocked_reason": ( + None if can_claim_fully_closed else + "At least one official precision/oracle layer is partial or blocked; report must expose blocked rows instead of claiming full closure." + ), + "license_boundary": "PyJHora is black-box evidence only; jyotishganit MIT assets may be referenced/reused within existing project policy.", + }, + "next_actions": [ + "Run --live-official-full-snapshot when foreground VedAstro budget/network credentials are available, then promote VedAstro only if the full official chart is stable.", + "Keep PyJHora as artifact-backed oracle evidence; do not import AGPL implementation code.", + "If a report needs full closure language, require all oracle ledger rows to be ok and the master oracle dashboard open_tasks to be 0.", + ], + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# External Official Sanity / Oracle Closure", + "", + f"Generated: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- ok_count: `{summary['ok_count']}`", + f"- partial_count: `{summary['partial_count']}`", + f"- blocked_count: `{summary['blocked_count']}`", + f"- total_tasks: `{summary.get('total_tasks')}`", + f"- external_verified_tasks: `{summary.get('external_verified_tasks')}`", + f"- open_tasks: `{summary.get('open_tasks')}`", + "", + "## Oracle Ledger", + "", + "| oracle | status | role | verdict |", + "|---|---|---|---|", + ] + for key, row in report["oracle_ledger"].items(): + lines.append(f"| `{key}` | `{row.get('status')}` | `{row.get('role')}` | `{row.get('verdict')}` |") + lines.extend([ + "", + "## Honesty Boundary", + "", + f"- can_claim_fully_closed: `{str(report['honesty_boundary']['can_claim_fully_closed']).lower()}`", + f"- can_claim_high_rigor_with_blocks: `{str(report['honesty_boundary']['can_claim_high_rigor_with_blocks']).lower()}`", + f"- blocked_reason: {report['honesty_boundary']['blocked_reason']}", + "", + "## Next Actions", + "", + ]) + lines.extend(f"- {item}" for item in report["next_actions"]) + lines.append("") + return "\n".join(lines) + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Audit VedAstro/PyJHora/jyotishganit sanity closure") + parser.add_argument("--dasha-oracle-file", default=DEFAULT_DASHA_ORACLE_FILE) + parser.add_argument("--tajika-oracle-file", default=DEFAULT_TAJIKA_ORACLE_FILE) + parser.add_argument( + "--live-official-full-snapshot", + action="store_true", + help="Run the heavy VedAstro official full snapshot probe; default stays non-blocking for CI.", + ) + parser.add_argument("--format", choices=["json", "markdown"], default="json") + parser.add_argument("--output") + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv) + report = build_report( + args.dasha_oracle_file, + args.tajika_oracle_file, + live_official_full_snapshot=args.live_official_full_snapshot, + ) + text = json.dumps(report, ensure_ascii=False, indent=2) if args.format == "json" else render_markdown(report) + if args.output: + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(text, encoding="utf-8") + print(text) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/run_quality_gate.py b/scripts/run_quality_gate.py index 02d34682..fc6e6de6 100644 --- a/scripts/run_quality_gate.py +++ b/scripts/run_quality_gate.py @@ -29,6 +29,7 @@ EXTRA_COMPILE_TARGETS = [ ROOT / "mcp_server.py", ROOT / "scripts" / "audit_fragments.py", ROOT / "scripts" / "dasha_reference_audit.py", + ROOT / "scripts" / "external_oracle_sanity_closure.py", ROOT / "scripts" / "oracle_boundary_audit.py", ROOT / "scripts" / "oracle_collection_queue.py", ROOT / "scripts" / "oracle_evidence_validator.py", @@ -47,6 +48,7 @@ CORE_PYTEST_TARGETS = [ "tests/test_transit_trigger.py", "tests/test_oracle_collection_queue.py", "tests/test_oracle_evidence_validator.py", + "tests/test_external_oracle_sanity_closure.py", ] RELEASE_CRITICAL_UNTRACKED_PATHS = [ @@ -192,6 +194,13 @@ ORACLE_BOUNDARY_AUDIT_CMD = [ "references/oracle/dasha_shadbala_oracle_cases.json", ] +EXTERNAL_ORACLE_SANITY_CLOSURE_CMD = [ + PYTHON, + "scripts/external_oracle_sanity_closure.py", + "--format", + "json", +] + ORACLE_COLLECTION_QUEUE_CMD = [ PYTHON, "scripts/oracle_collection_queue.py", @@ -497,6 +506,7 @@ def main() -> int: run(DASHA_REFERENCE_AUDIT_CMD) if not profile["skip_oracle_audit"]: run(ORACLE_BOUNDARY_AUDIT_CMD) + run(EXTERNAL_ORACLE_SANITY_CLOSURE_CMD) run_oracle_collection_queue_and_validator() if not profile["skip_yoga_logic"]: run([PYTHON, "scripts/validate_logic_v2.py"], optional=True) diff --git a/tests/test_external_oracle_sanity_closure.py b/tests/test_external_oracle_sanity_closure.py new file mode 100644 index 00000000..6ebd9360 --- /dev/null +++ b/tests/test_external_oracle_sanity_closure.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def _run_closure(*args: str) -> dict: + completed = subprocess.run( + [sys.executable, "scripts/external_oracle_sanity_closure.py", *args], + cwd=ROOT, + text=True, + capture_output=True, + timeout=180, + check=False, + ) + assert completed.returncode == 0, completed.stderr or completed.stdout + return json.loads(completed.stdout) + + +def test_external_official_sanity_closure_reports_all_three_oracles() -> None: + report = _run_closure("--format", "json") + + assert report["scope"] == "external_official_sanity_oracle_closure" + assert report["summary"]["required_oracles"] == ["VedAstro", "PyJHora", "jyotishganit"] + assert report["summary"]["live_official_full_snapshot"] is False + assert set(report["oracle_ledger"]) == {"vedastro", "pyjhora", "jyotishganit"} + + vedastro = report["oracle_ledger"]["vedastro"] + assert vedastro["role"] == "official_precision_sanity" + assert "status" in vedastro + assert "verdict" in vedastro + assert vedastro["live_official_full_snapshot"] is False + assert vedastro["snapshot_status"] == "not_run_default_non_blocking" + assert "fine_calc_blocked" in vedastro + + pyjhora = report["oracle_ledger"]["pyjhora"] + assert pyjhora["role"] == "black_box_external_oracle" + assert pyjhora["artifact_count"] >= 8 + assert pyjhora["packet_count"] >= 8 + assert pyjhora["license_boundary"] == "black_box_artifacts_only_no_agpl_code_import" + + jyotishganit = report["oracle_ledger"]["jyotishganit"] + assert jyotishganit["role"] == "mit_reference_layer" + assert jyotishganit["license"] == "MIT" + assert jyotishganit["status"] in {"ok", "partial", "blocked"} + + assert report["honesty_boundary"]["can_claim_fully_closed"] is False + assert report["honesty_boundary"]["can_claim_high_rigor_with_blocks"] is True + + +def test_external_official_sanity_closure_marks_live_vedastro_blocked_when_sanity_fails() -> None: + report = _run_closure("--format", "json") + vedastro = report["oracle_ledger"]["vedastro"] + + if vedastro["status"] == "ok": + assert vedastro["fine_calc_blocked"] is False + else: + assert vedastro["fine_calc_blocked"] is True + assert report["summary"]["blocked_count"] >= 1 + + +def test_external_official_sanity_closure_documents_live_snapshot_flag() -> None: + completed = subprocess.run( + [sys.executable, "scripts/external_oracle_sanity_closure.py", "--help"], + cwd=ROOT, + text=True, + capture_output=True, + timeout=30, + check=False, + ) + + assert completed.returncode == 0 + assert "--live-official-full-snapshot" in completed.stdout