diff --git a/README.md b/README.md index b6b9dcf5..fcfab761 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,17 @@ python3 scripts/dasha_oracle_closure_status.py \ 当前第一优先级是 `external_template_steve_jobs_dasha_lahiri`。该状态板只要求 `target.vimshottari_start_date` 和外部证据 metadata,不要求同时填完 Shadbala 七曜六分量;这样可以先完成 Dasha oracle 的第一条闭环,再单独推进 Shadbala 绝对值闭环。 +Shadbala 外部绝对值闭环使用独立状态板,专门追踪七曜的六分量与总 Rupa: + +```bash +python3 scripts/shadbala_oracle_closure_status.py \ + --oracle-file references/oracle/dasha_shadbala_oracle_cases.json \ + --format markdown \ + --output docs/benchmark/shadbala_external_absolute_value_closure_status.md +``` + +当前第一优先级是 `external_template_redacted_place_shadbala_raman`。必须填写 Sun/Moon/Mars/Mercury/Jupiter/Venus/Saturn 的 `sthana`、`dig`、`kala`、`chesta`、`naisargika`、`drik`、`total_rupa`;验证器会检查分量和总分,不允许用一个全局倍率把本地输出硬缩放成外部值。 + Tajika/Sahams 年运系统使用独立的外部 oracle 队列,专门追踪 Varshaphala、太阳回归、Muntha、Year Lord、Mudda Dasha、Sahams 与 Tajika Yogas 的外部验证状态: ```bash diff --git a/docs/benchmark/shadbala_external_absolute_value_closure_status.json b/docs/benchmark/shadbala_external_absolute_value_closure_status.json new file mode 100644 index 00000000..2d831885 --- /dev/null +++ b/docs/benchmark/shadbala_external_absolute_value_closure_status.json @@ -0,0 +1,123 @@ +{ + "scope": "shadbala_external_absolute_value_closure_status", + "schema_version": 1, + "summary": { + "shadbala_task_count": 4, + "external_verified_shadbala_tasks": 0, + "can_claim_shadbala_absolute_closure": false, + "production_tuning_allowed": false, + "required_planets": [ + "Sun", + "Moon", + "Mars", + "Mercury", + "Jupiter", + "Venus", + "Saturn" + ], + "required_components": [ + "sthana", + "dig", + "kala", + "chesta", + "naisargika", + "drik", + "total_rupa" + ] + }, + "first_priority": { + "case_id": "template_redacted_place_shadbala_raman", + "capture_id": "external_template_redacted_place_shadbala_raman", + "packet_path": "references/oracle/artifacts/pending_packets/external_template_redacted_place_shadbala_raman.json", + "birth": { + "year": 1980, + "month": 1, + "day": 1, + "hour": 12, + "minute": 0, + "second": 0, + "lat": 36.466667, + "lon": 114.2, + "tz": 8 + }, + "settings": { + "ayanamsa": "raman", + "node_mode": "mean" + }, + "required_target_fields": [ + "target.shadbala_components" + ], + "missing_fields": [ + "metadata.tool_name", + "metadata.tool_version_or_url", + "metadata.capture_date", + "metadata.operator_note", + "metadata.source_artifact", + "target.shadbala_components.Sun.sthana", + "target.shadbala_components.Sun.dig", + "target.shadbala_components.Sun.kala", + "target.shadbala_components.Sun.chesta", + "target.shadbala_components.Sun.naisargika", + "target.shadbala_components.Sun.drik", + "target.shadbala_components.Sun.total_rupa", + "target.shadbala_components.Moon.sthana", + "target.shadbala_components.Moon.dig", + "target.shadbala_components.Moon.kala", + "target.shadbala_components.Moon.chesta", + "target.shadbala_components.Moon.naisargika", + "target.shadbala_components.Moon.drik", + "target.shadbala_components.Moon.total_rupa", + "target.shadbala_components.Mars.sthana", + "target.shadbala_components.Mars.dig", + "target.shadbala_components.Mars.kala", + "target.shadbala_components.Mars.chesta", + "target.shadbala_components.Mars.naisargika", + "target.shadbala_components.Mars.drik", + "target.shadbala_components.Mars.total_rupa", + "target.shadbala_components.Mercury.sthana", + "target.shadbala_components.Mercury.dig", + "target.shadbala_components.Mercury.kala", + "target.shadbala_components.Mercury.chesta", + "target.shadbala_components.Mercury.naisargika", + "target.shadbala_components.Mercury.drik", + "target.shadbala_components.Mercury.total_rupa", + "target.shadbala_components.Jupiter.sthana", + "target.shadbala_components.Jupiter.dig", + "target.shadbala_components.Jupiter.kala", + "target.shadbala_components.Jupiter.chesta", + "target.shadbala_components.Jupiter.naisargika", + "target.shadbala_components.Jupiter.drik", + "target.shadbala_components.Jupiter.total_rupa", + "target.shadbala_components.Venus.sthana", + "target.shadbala_components.Venus.dig", + "target.shadbala_components.Venus.kala", + "target.shadbala_components.Venus.chesta", + "target.shadbala_components.Venus.naisargika", + "target.shadbala_components.Venus.drik", + "target.shadbala_components.Venus.total_rupa", + "target.shadbala_components.Saturn.sthana", + "target.shadbala_components.Saturn.dig", + "target.shadbala_components.Saturn.kala", + "target.shadbala_components.Saturn.chesta", + "target.shadbala_components.Saturn.naisargika", + "target.shadbala_components.Saturn.drik", + "target.shadbala_components.Saturn.total_rupa" + ], + "external_sources": [ + "JHora Shadbala component table screenshot", + "PyJHora black-box shadbala output", + "documented printed/software Shadbala example" + ], + "artifact_policy": "Save redacted screenshots or stdout snippets under references/oracle/artifacts/.", + "reject_global_scaling": true, + "apply_command": "python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --apply-packet references/oracle/artifacts/pending_packets/external_template_redacted_place_shadbala_raman.json --format json", + "validate_command": "python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --format json > /tmp/jyotish_oracle_queue_filled.json && python3 scripts/oracle_evidence_validator.py --queue-file /tmp/jyotish_oracle_queue_filled.json" + }, + "next_actions": [ + "Fill all seven planets with Sthana, Dig, Kala, Chesta, Naisargika, Drik and total_rupa from an external oracle.", + "Do not use a single global multiplier to force totals; validator checks component sums.", + "Set status to external_verified only after artifact path and all Shadbala targets are filled.", + "Apply the packet, regenerate the queue, and run oracle_evidence_validator.py." + ], + "boundary": "This board isolates Shadbala absolute values. Dasha boundary dates are a separate closure task. Production tuning remains forbidden until external component-level evidence is complete." +} \ No newline at end of file diff --git a/docs/benchmark/shadbala_external_absolute_value_closure_status.md b/docs/benchmark/shadbala_external_absolute_value_closure_status.md new file mode 100644 index 00000000..20ef1672 --- /dev/null +++ b/docs/benchmark/shadbala_external_absolute_value_closure_status.md @@ -0,0 +1,41 @@ +# Shadbala External Absolute-Value Closure Status + +- shadbala_task_count: `4` +- external_verified_shadbala_tasks: `0` +- can_claim_shadbala_absolute_closure: `false` +- production_tuning_allowed: `false` + +## First Priority Packet + +- case_id: `template_redacted_place_shadbala_raman` +- capture_id: `external_template_redacted_place_shadbala_raman` +- packet_path: `references/oracle/artifacts/pending_packets/external_template_redacted_place_shadbala_raman.json` +- required_target_fields: `target.shadbala_components` +- missing_fields: `metadata.tool_name, metadata.tool_version_or_url, metadata.capture_date, metadata.operator_note, metadata.source_artifact, target.shadbala_components.Sun.sthana, target.shadbala_components.Sun.dig, target.shadbala_components.Sun.kala, target.shadbala_components.Sun.chesta, target.shadbala_components.Sun.naisargika, target.shadbala_components.Sun.drik, target.shadbala_components.Sun.total_rupa, target.shadbala_components.Moon.sthana, target.shadbala_components.Moon.dig, target.shadbala_components.Moon.kala, target.shadbala_components.Moon.chesta, target.shadbala_components.Moon.naisargika, target.shadbala_components.Moon.drik, target.shadbala_components.Moon.total_rupa, target.shadbala_components.Mars.sthana, target.shadbala_components.Mars.dig, target.shadbala_components.Mars.kala, target.shadbala_components.Mars.chesta, target.shadbala_components.Mars.naisargika, target.shadbala_components.Mars.drik, target.shadbala_components.Mars.total_rupa, target.shadbala_components.Mercury.sthana, target.shadbala_components.Mercury.dig, target.shadbala_components.Mercury.kala, target.shadbala_components.Mercury.chesta, target.shadbala_components.Mercury.naisargika, target.shadbala_components.Mercury.drik, target.shadbala_components.Mercury.total_rupa, target.shadbala_components.Jupiter.sthana, target.shadbala_components.Jupiter.dig, target.shadbala_components.Jupiter.kala, target.shadbala_components.Jupiter.chesta, target.shadbala_components.Jupiter.naisargika, target.shadbala_components.Jupiter.drik, target.shadbala_components.Jupiter.total_rupa, target.shadbala_components.Venus.sthana, target.shadbala_components.Venus.dig, target.shadbala_components.Venus.kala, target.shadbala_components.Venus.chesta, target.shadbala_components.Venus.naisargika, target.shadbala_components.Venus.drik, target.shadbala_components.Venus.total_rupa, target.shadbala_components.Saturn.sthana, target.shadbala_components.Saturn.dig, target.shadbala_components.Saturn.kala, target.shadbala_components.Saturn.chesta, target.shadbala_components.Saturn.naisargika, target.shadbala_components.Saturn.drik, target.shadbala_components.Saturn.total_rupa` +- reject_global_scaling: `true` + +## Required Matrix + +- planets: `Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn` +- components: `sthana, dig, kala, chesta, naisargika, drik, total_rupa` + +## Commands + +```bash +python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --apply-packet references/oracle/artifacts/pending_packets/external_template_redacted_place_shadbala_raman.json --format json +``` + +```bash +python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --format json > /tmp/jyotish_oracle_queue_filled.json && python3 scripts/oracle_evidence_validator.py --queue-file /tmp/jyotish_oracle_queue_filled.json +``` + +## Next Actions + +- Fill all seven planets with Sthana, Dig, Kala, Chesta, Naisargika, Drik and total_rupa from an external oracle. +- Do not use a single global multiplier to force totals; validator checks component sums. +- Set status to external_verified only after artifact path and all Shadbala targets are filled. +- Apply the packet, regenerate the queue, and run oracle_evidence_validator.py. + +## Boundary + +This board isolates Shadbala absolute values. Dasha boundary dates are a separate closure task. Production tuning remains forbidden until external component-level evidence is complete. diff --git a/scripts/shadbala_oracle_closure_status.py b/scripts/shadbala_oracle_closure_status.py new file mode 100644 index 00000000..ed628911 --- /dev/null +++ b/scripts/shadbala_oracle_closure_status.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +"""Report the shortest path to Shadbala external absolute-value closure.""" + +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] +PYTHON = sys.executable +FIRST_PRIORITY_CASE_ID = "template_redacted_place_shadbala_raman" +SHADBALA_TARGET_FIELD = "target.shadbala_components" +REQUIRED_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] +REQUIRED_COMPONENTS = ["sthana", "dig", "kala", "chesta", "naisargika", "drik", "total_rupa"] + + +def _run_json(command: list[str]) -> dict[str, Any]: + completed = subprocess.run( + command, + cwd=ROOT, + text=True, + capture_output=True, + timeout=60, + check=False, + ) + if completed.returncode != 0: + raise RuntimeError(completed.stderr.strip() or completed.stdout.strip()) + return json.loads(completed.stdout) + + +def _metadata_missing(packet: dict[str, Any]) -> list[str]: + metadata = packet.get("metadata", {}) + missing: list[str] = [] + for field in packet.get("required_metadata_fields", []): + value = metadata.get(field) + if value is None or value == "" or value == [] or value == {}: + missing.append(f"metadata.{field}") + source_artifact = metadata.get("source_artifact") + if source_artifact in {"references/oracle/artifacts/", "references/oracle/artifacts", "", None}: + if "metadata.source_artifact" not in missing: + missing.append("metadata.source_artifact") + return missing + + +def _shadbala_missing(packet: dict[str, Any]) -> list[str]: + placeholders = packet.get("target_placeholders", {}) + value = placeholders.get(SHADBALA_TARGET_FIELD) + missing: list[str] = [] + if not isinstance(value, dict): + return [ + f"{SHADBALA_TARGET_FIELD}.{planet}.{component}" + for planet in REQUIRED_PLANETS + for component in REQUIRED_COMPONENTS + ] + for planet in REQUIRED_PLANETS: + row = value.get(planet) + if not isinstance(row, dict): + missing.extend(f"{SHADBALA_TARGET_FIELD}.{planet}.{component}" for component in REQUIRED_COMPONENTS) + continue + for component in REQUIRED_COMPONENTS: + component_value = row.get(component) + if component_value is None or component_value == "" or component_value == [] or component_value == {}: + missing.append(f"{SHADBALA_TARGET_FIELD}.{planet}.{component}") + return missing + + +def _apply_command(packet_path: str, oracle_file: str) -> str: + return ( + "python3 scripts/oracle_collection_queue.py " + f"--oracle-file {oracle_file} " + f"--apply-packet {packet_path} " + "--format json" + ) + + +def _validate_command(oracle_file: str) -> str: + return ( + "python3 scripts/oracle_collection_queue.py " + f"--oracle-file {oracle_file} --format json > /tmp/jyotish_oracle_queue_filled.json && " + "python3 scripts/oracle_evidence_validator.py --queue-file /tmp/jyotish_oracle_queue_filled.json" + ) + + +def build_status(oracle_file: str) -> dict[str, Any]: + queue = _run_json([PYTHON, "scripts/oracle_collection_queue.py", "--oracle-file", oracle_file, "--format", "json"]) + shadbala_tasks = [ + task for task in queue.get("tasks", []) + if SHADBALA_TARGET_FIELD in task.get("target_fields", []) + ] + priority = next((task for task in shadbala_tasks if task.get("case_id") == FIRST_PRIORITY_CASE_ID), None) + if priority is None and shadbala_tasks: + priority = shadbala_tasks[0] + if priority is None: + raise RuntimeError("No Shadbala target task found") + + packet = priority["evidence_packet"] + capture_id = packet["capture_id"] + packet_path = f"references/oracle/artifacts/pending_packets/{capture_id}.json" + missing_fields = _metadata_missing(packet) + _shadbala_missing(packet) + external_verified = [ + task for task in shadbala_tasks + if task.get("status") == "external_verified" and not _shadbala_missing(task.get("evidence_packet", {})) + ] + + return { + "scope": "shadbala_external_absolute_value_closure_status", + "schema_version": 1, + "summary": { + "shadbala_task_count": len(shadbala_tasks), + "external_verified_shadbala_tasks": len(external_verified), + "can_claim_shadbala_absolute_closure": bool(shadbala_tasks) and len(external_verified) == len(shadbala_tasks), + "production_tuning_allowed": False, + "required_planets": REQUIRED_PLANETS, + "required_components": REQUIRED_COMPONENTS, + }, + "first_priority": { + "case_id": priority["case_id"], + "capture_id": capture_id, + "packet_path": packet_path, + "birth": priority.get("birth", {}), + "settings": priority.get("settings", {}), + "required_target_fields": [SHADBALA_TARGET_FIELD], + "missing_fields": missing_fields, + "external_sources": [ + "JHora Shadbala component table screenshot", + "PyJHora black-box shadbala output", + "documented printed/software Shadbala example", + ], + "artifact_policy": "Save redacted screenshots or stdout snippets under references/oracle/artifacts/.", + "reject_global_scaling": True, + "apply_command": _apply_command(packet_path, oracle_file), + "validate_command": _validate_command(oracle_file), + }, + "next_actions": [ + "Fill all seven planets with Sthana, Dig, Kala, Chesta, Naisargika, Drik and total_rupa from an external oracle.", + "Do not use a single global multiplier to force totals; validator checks component sums.", + "Set status to external_verified only after artifact path and all Shadbala targets are filled.", + "Apply the packet, regenerate the queue, and run oracle_evidence_validator.py.", + ], + "boundary": ( + "This board isolates Shadbala absolute values. Dasha boundary dates are a separate closure task. " + "Production tuning remains forbidden until external component-level evidence is complete." + ), + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + first = report["first_priority"] + lines = [ + "# Shadbala External Absolute-Value Closure Status", + "", + f"- shadbala_task_count: `{summary['shadbala_task_count']}`", + f"- external_verified_shadbala_tasks: `{summary['external_verified_shadbala_tasks']}`", + f"- can_claim_shadbala_absolute_closure: `{str(summary['can_claim_shadbala_absolute_closure']).lower()}`", + f"- production_tuning_allowed: `{str(summary['production_tuning_allowed']).lower()}`", + "", + "## First Priority Packet", + "", + f"- case_id: `{first['case_id']}`", + f"- capture_id: `{first['capture_id']}`", + f"- packet_path: `{first['packet_path']}`", + f"- required_target_fields: `{', '.join(first['required_target_fields'])}`", + f"- missing_fields: `{', '.join(first['missing_fields'])}`", + f"- reject_global_scaling: `{str(first['reject_global_scaling']).lower()}`", + "", + "## Required Matrix", + "", + f"- planets: `{', '.join(summary['required_planets'])}`", + f"- components: `{', '.join(summary['required_components'])}`", + "", + "## Commands", + "", + "```bash", + first["apply_command"], + "```", + "", + "```bash", + first["validate_command"], + "```", + "", + "## Next Actions", + "", + ] + lines.extend(f"- {item}" for item in report["next_actions"]) + lines.extend(["", "## Boundary", "", report["boundary"], ""]) + return "\n".join(lines) + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Report Shadbala external absolute-value closure status") + parser.add_argument("--oracle-file", default="references/oracle/dasha_shadbala_oracle_cases.json") + parser.add_argument("--format", choices=["json", "markdown"], default="json") + parser.add_argument("--output", help="Optional output path") + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv) + report = build_status(args.oracle_file) + 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/tests/test_shadbala_oracle_closure_status.py b/tests/test_shadbala_oracle_closure_status.py new file mode 100644 index 00000000..8edfd22a --- /dev/null +++ b/tests/test_shadbala_oracle_closure_status.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Tests for the shortest Shadbala external absolute-value closure status board.""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def run_status(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + "scripts/shadbala_oracle_closure_status.py", + "--oracle-file", + "references/oracle/dasha_shadbala_oracle_cases.json", + *args, + ], + cwd=ROOT, + text=True, + capture_output=True, + timeout=60, + check=False, + ) + + +def test_shadbala_oracle_closure_status_identifies_first_absolute_value_packet() -> None: + completed = run_status("--format", "json") + + assert completed.returncode == 0, completed.stderr or completed.stdout + report = json.loads(completed.stdout) + assert report["scope"] == "shadbala_external_absolute_value_closure_status" + assert report["schema_version"] == 1 + assert report["summary"]["shadbala_task_count"] == 4 + assert report["summary"]["external_verified_shadbala_tasks"] == 0 + assert report["summary"]["can_claim_shadbala_absolute_closure"] is False + assert report["summary"]["required_planets"] == ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] + assert report["summary"]["required_components"] == ["sthana", "dig", "kala", "chesta", "naisargika", "drik", "total_rupa"] + assert report["first_priority"]["case_id"] == "template_redacted_place_shadbala_raman" + assert report["first_priority"]["capture_id"] == "external_template_redacted_place_shadbala_raman" + assert report["first_priority"]["required_target_fields"] == ["target.shadbala_components"] + assert "metadata.tool_name" in report["first_priority"]["missing_fields"] + assert "target.shadbala_components.Sun.sthana" in report["first_priority"]["missing_fields"] + assert "target.shadbala_components.Saturn.total_rupa" in report["first_priority"]["missing_fields"] + assert report["first_priority"]["reject_global_scaling"] is True + assert report["first_priority"]["validate_command"] + + +def test_shadbala_oracle_closure_status_markdown_can_be_written(tmp_path: Path) -> None: + output = tmp_path / "shadbala_status.md" + completed = run_status("--format", "markdown", "--output", str(output)) + + assert completed.returncode == 0, completed.stderr or completed.stdout + assert output.exists() + markdown = output.read_text(encoding="utf-8") + assert "# Shadbala External Absolute-Value Closure Status" in markdown + assert "can_claim_shadbala_absolute_closure: `false`" in markdown + assert "external_template_redacted_place_shadbala_raman" in markdown + assert "target.shadbala_components.Sun.sthana" in markdown