diff --git a/README.md b/README.md index 54942941..3fc65f61 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,17 @@ python3 scripts/dasha_oracle_closure_status.py \ 当前第一优先级是 `external_template_steve_jobs_dasha_lahiri`。该状态板只要求 `target.vimshottari_start_date` 和外部证据 metadata,不要求同时填完 Shadbala 七曜六分量;这样可以先完成 Dasha oracle 的第一条闭环,再单独推进 Shadbala 绝对值闭环。 +第一条 Dasha 证据包的交互辅助命令: + +```bash +python3 scripts/first_oracle_packet_assistant.py \ + --front dasha \ + --format markdown \ + --output docs/benchmark/first_dasha_oracle_packet_assistant.md +``` + +该助手不会生成或猜测 JHora/PyJHora 真值,只会列出 `dasha_steve_jobs_lahiri_first_packet_only.json` 当前还缺哪些字段、可用外部来源、apply 命令和 validator 命令。 + Shadbala 外部绝对值闭环使用独立状态板,专门追踪七曜的六分量与总 Rupa: ```bash diff --git a/docs/benchmark/first_dasha_oracle_packet_assistant.json b/docs/benchmark/first_dasha_oracle_packet_assistant.json new file mode 100644 index 00000000..dc53d19c --- /dev/null +++ b/docs/benchmark/first_dasha_oracle_packet_assistant.json @@ -0,0 +1,26 @@ +{ + "scope": "first_external_oracle_packet_assistant", + "schema_version": 1, + "front": "dasha", + "case_id": "template_steve_jobs_dasha_lahiri", + "capture_id": "external_template_steve_jobs_dasha_lahiri", + "operator_card": "docs/benchmark/dasha_steve_jobs_first_packet_operator_card.md", + "packet_template": "references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json", + "missing_fields": [ + "metadata.tool_name", + "metadata.tool_version_or_url", + "metadata.capture_date", + "metadata.operator_note", + "metadata.source_artifact", + "target.vimshottari_start_date" + ], + "ready_to_apply": false, + "external_sources": [ + "JHora Vimshottari Dasha screen", + "PyJHora black-box dasha output", + "documented printed/software example" + ], + "apply_command": "python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --apply-packet references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.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", + "boundary": "This assistant only reports what to fill. It must not invent external oracle values, must not use local engine output as evidence, and must not copy incompatible external code." +} \ No newline at end of file diff --git a/docs/benchmark/first_dasha_oracle_packet_assistant.md b/docs/benchmark/first_dasha_oracle_packet_assistant.md new file mode 100644 index 00000000..223cbbfa --- /dev/null +++ b/docs/benchmark/first_dasha_oracle_packet_assistant.md @@ -0,0 +1,39 @@ +# First External Oracle Packet Assistant + +- front: `dasha` +- case_id: `template_steve_jobs_dasha_lahiri` +- capture_id: `external_template_steve_jobs_dasha_lahiri` +- ready_to_apply: `false` +- operator_card: `docs/benchmark/dasha_steve_jobs_first_packet_operator_card.md` +- packet_template: `references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json` + +## Missing Fields + +- `metadata.tool_name` +- `metadata.tool_version_or_url` +- `metadata.capture_date` +- `metadata.operator_note` +- `metadata.source_artifact` +- `target.vimshottari_start_date` + +## External Sources + +- JHora Vimshottari Dasha screen +- PyJHora black-box dasha output +- documented printed/software example + +## Apply + +```bash +python3 scripts/oracle_collection_queue.py --oracle-file references/oracle/dasha_shadbala_oracle_cases.json --apply-packet references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json --format json +``` + +## Validate + +```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 +``` + +## Boundary + +This assistant only reports what to fill. It must not invent external oracle values, must not use local engine output as evidence, and must not copy incompatible external code. diff --git a/scripts/first_oracle_packet_assistant.py b/scripts/first_oracle_packet_assistant.py new file mode 100644 index 00000000..3d68b0f2 --- /dev/null +++ b/scripts/first_oracle_packet_assistant.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""Assist filling the first external oracle packet without inventing oracle values.""" + +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 + +FRONTS = { + "dasha": { + "status_command": [ + PYTHON, + "scripts/dasha_oracle_closure_status.py", + "--oracle-file", + "references/oracle/dasha_shadbala_oracle_cases.json", + "--format", + "json", + ], + "operator_card": "docs/benchmark/dasha_steve_jobs_first_packet_operator_card.md", + "packet_template": "references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json", + "external_sources": [ + "JHora Vimshottari Dasha screen", + "PyJHora black-box dasha output", + "documented printed/software example", + ], + } +} + + +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 _packet_missing(packet_path: str) -> list[str]: + packet = json.loads((ROOT / packet_path).read_text(encoding="utf-8")) + missing: list[str] = [] + metadata = packet.get("metadata", {}) + 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}") + if metadata.get("source_artifact") in {"references/oracle/artifacts/", "references/oracle/artifacts", "", None}: + if "metadata.source_artifact" not in missing: + missing.append("metadata.source_artifact") + for field, value in packet.get("target_placeholders", {}).items(): + if value is None or value == "" or value == [] or value == {}: + missing.append(field) + return missing + + +def build_report(front: str) -> dict[str, Any]: + if front not in FRONTS: + raise RuntimeError(f"Unsupported front: {front}") + config = FRONTS[front] + status = _run_json(config["status_command"]) + first = status["first_priority"] + packet_missing = _packet_missing(config["packet_template"]) + return { + "scope": "first_external_oracle_packet_assistant", + "schema_version": 1, + "front": front, + "case_id": first["case_id"], + "capture_id": first["capture_id"], + "operator_card": config["operator_card"], + "packet_template": config["packet_template"], + "missing_fields": packet_missing, + "ready_to_apply": not packet_missing, + "external_sources": config["external_sources"], + "apply_command": first["apply_command"].replace(first["packet_path"], config["packet_template"]), + "validate_command": first["validate_command"], + "boundary": ( + "This assistant only reports what to fill. It must not invent external oracle values, " + "must not use local engine output as evidence, and must not copy incompatible external code." + ), + } + + +def render_markdown(report: dict[str, Any]) -> str: + lines = [ + "# First External Oracle Packet Assistant", + "", + f"- front: `{report['front']}`", + f"- case_id: `{report['case_id']}`", + f"- capture_id: `{report['capture_id']}`", + f"- ready_to_apply: `{str(report['ready_to_apply']).lower()}`", + f"- operator_card: `{report['operator_card']}`", + f"- packet_template: `{report['packet_template']}`", + "", + "## Missing Fields", + "", + ] + lines.extend(f"- `{field}`" for field in report["missing_fields"]) + lines.extend( + [ + "", + "## External Sources", + "", + ] + ) + lines.extend(f"- {source}" for source in report["external_sources"]) + lines.extend( + [ + "", + "## Apply", + "", + "```bash", + report["apply_command"], + "```", + "", + "## Validate", + "", + "```bash", + report["validate_command"], + "```", + "", + "## Boundary", + "", + report["boundary"], + "", + ] + ) + return "\n".join(lines) + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Assist the first external oracle packet") + parser.add_argument("--front", choices=sorted(FRONTS), required=True) + 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_report(args.front) + 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_first_oracle_packet_assistant.py b/tests/test_first_oracle_packet_assistant.py new file mode 100644 index 00000000..a3de40bc --- /dev/null +++ b/tests/test_first_oracle_packet_assistant.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Tests for the first external-oracle packet assistant.""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def run_assistant(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + "scripts/first_oracle_packet_assistant.py", + "--front", + "dasha", + *args, + ], + cwd=ROOT, + text=True, + capture_output=True, + timeout=60, + check=False, + ) + + +def test_first_oracle_packet_assistant_reports_dasha_next_fields() -> None: + completed = run_assistant("--format", "json") + + assert completed.returncode == 0, completed.stderr or completed.stdout + report = json.loads(completed.stdout) + assert report["scope"] == "first_external_oracle_packet_assistant" + assert report["front"] == "dasha" + assert report["case_id"] == "template_steve_jobs_dasha_lahiri" + assert report["operator_card"].endswith("dasha_steve_jobs_first_packet_operator_card.md") + assert report["packet_template"].endswith("dasha_steve_jobs_lahiri_first_packet_only.json") + assert report["missing_fields"] == [ + "metadata.tool_name", + "metadata.tool_version_or_url", + "metadata.capture_date", + "metadata.operator_note", + "metadata.source_artifact", + "target.vimshottari_start_date", + ] + assert report["ready_to_apply"] is False + assert "JHora" in " ".join(report["external_sources"]) + + +def test_first_oracle_packet_assistant_markdown_can_be_written(tmp_path: Path) -> None: + output = tmp_path / "assistant.md" + completed = run_assistant("--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 "# First External Oracle Packet Assistant" in markdown + assert "template_steve_jobs_dasha_lahiri" in markdown + assert "target.vimshottari_start_date" in markdown + assert "ready_to_apply: `false`" in markdown