diff --git a/README.md b/README.md index 4b089fa9..1bb14682 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/) -[![Techniques](https://img.shields.io/badge/techniques-79-blueviolet)](references/technique_registry.json) -[![Covered](https://img.shields.io/badge/covered-70-green)](references/technique_registry.json) -[![Complete](https://img.shields.io/badge/complete-9-brightgreen)](references/technique_registry.json) +[![Techniques](https://img.shields.io/badge/techniques-89-blueviolet)](references/technique_registry.json) +[![Covered](https://img.shields.io/badge/covered-79-green)](references/technique_registry.json) +[![Complete](https://img.shields.io/badge/complete-10-brightgreen)](references/technique_registry.json) [![Partial](https://img.shields.io/badge/partial-0-lightgrey)](references/technique_registry.json) --- @@ -31,7 +31,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, auditable full-chart readings. It is NOT a simple ephemeris calculator — it is a multi-stage interpretive pipeline that: 1. **Computes** divisional charts (D1/D9/D10/...) via Swiss Ephemeris -2. **Runs** 79 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) +2. **Runs** 89 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) 3. **Routes** the analysis through strict workflow paths depending on question type (career / relationship / wealth / timing) 4. **Audits** every technique used — declaring what was called, what is complete/covered, and which limitations affect confidence 5. **Degrades gracefully** — limitations are labeled, not silently over-promising @@ -45,7 +45,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, audit | Technique Audit Table (confidence labeling) | ✅ | ❌ | ❌ | ❌ | | Capability degradation (limits are explicit) | ✅ | ❌ | ❌ | ❌ | | MEVG external verification gates | ✅ | ❌ | ❌ | ❌ | -| 79 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | +| 89 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | | Traditional algorithm benchmarked | ✅ mixed depth | ✅ | ✅ | ✅ | | Docker / MCP Server | ✅ | ❌ | ✅ | ❌ | | English docs / PyPI package | ✅ in progress | ✅ | ✅ | ✅ | @@ -442,7 +442,11 @@ The AI does NOT require the user to name techniques (e.g., "Chara Dasha"). It au ## Technique Coverage -Current count: **68 techniques** (58 covered, 10 complete, 0 partial, 0 missing) +Current registry count: **89 techniques** (79 covered, 10 complete, 0 partial, 0 missing). + +The table below lists representative high-value entries. Treat +`references/technique_registry.json` as the source of truth for the full +machine-readable registry. | Technique | Status | Notes | |-----------|--------|-------| diff --git a/docs/research/ACTIVE_FRONTS.md b/docs/research/ACTIVE_FRONTS.md index 3a3f6601..e00da7c1 100644 --- a/docs/research/ACTIVE_FRONTS.md +++ b/docs/research/ACTIVE_FRONTS.md @@ -7,6 +7,7 @@ This file is the small index for the current engineering fronts that still drive - Before touching strict workflow or adjudicator logic, check: - `/Users/wuyongnaren/Documents/印度占星/docs/research/high_value_fragment_source_map_2026_06_28.md` - `/Users/wuyongnaren/Documents/印度占星/docs/research/worktree_fragment_triage_2026_06_28.md` + - `/Users/wuyongnaren/Documents/印度占星/docs/research/yoga_benchmark_case_promotion_audit_2026_06_28.md` - Treat repo truth as authoritative. - Treat `docs/research/local_drafts`, Gemini brain notes, Codex attachments, and WorkBuddy copies as candidate sources only. diff --git a/docs/research/worktree_fragment_triage_2026_06_28.md b/docs/research/worktree_fragment_triage_2026_06_28.md index abc291b6..7b680b02 100644 --- a/docs/research/worktree_fragment_triage_2026_06_28.md +++ b/docs/research/worktree_fragment_triage_2026_06_28.md @@ -56,8 +56,8 @@ Only promote this report later if a stable human-facing archive is needed. - Disposition: useful boundary values were promoted into `/Users/wuyongnaren/Documents/印度占星/references/oracle/dasha_shadbala_oracle_cases.json` as `template_bv_raman_vimshottari_boundary_series`. - Follow-up: collect source artifact metadata before promoting beyond `template_only`. - `/Users/wuyongnaren/Documents/印度占星/tests/test_yoga_benchmark_cases.py` - - Current state: smoke-style test ending in `assert True`. - - Better path: replace with concrete yoga names and expected detections before promotion. + - Disposition: promoted into a real benchmark with concrete `rule_id` expectations and metadata contract checks. + - See `/Users/wuyongnaren/Documents/印度占星/docs/research/yoga_benchmark_case_promotion_audit_2026_06_28.md`. ### Superseded or Needs Review diff --git a/docs/research/yoga_benchmark_case_promotion_audit_2026_06_28.md b/docs/research/yoga_benchmark_case_promotion_audit_2026_06_28.md new file mode 100644 index 00000000..7a20ad8d --- /dev/null +++ b/docs/research/yoga_benchmark_case_promotion_audit_2026_06_28.md @@ -0,0 +1,40 @@ +# Yoga Benchmark Case Promotion Audit - 2026-06-28 + +## Scope + +This pass converts the untracked `tests/test_yoga_benchmark_cases.py` scratch smoke test into an executable Yoga benchmark. + +## Problem With The Old Fragment + +The old file only verified that `detect_yogas_from_json()` returned a list and ended with `assert True`. It did not constrain any specific Yoga rule, category, strength, or metadata. + +## Promoted Test Contract + +The promoted test uses the same B.V. Raman-style static Aquarius chart but now asserts stable rule ids: + +- `dharma_karmadhipati` +- `chandra_yoga_exalted` +- `shakti_yoga_9_10_lord` +- `bvr_016_vesi_precise` +- `budha_shukra_yoga` +- `kalatra_yoga_venus_kendra` +- `raja_yoga_5_10_lord` +- `raja_yoga_4_10_lord` + +It also verifies that the metadata contract remains useful: + +- `category` +- `strength` +- `source` +- human-readable `combination` + +## Boundary + +- This is a deterministic engine benchmark, not a life prediction. +- It does not claim B.V. Raman biography validation. +- It guards the data-driven Yoga engine and rule metadata against silent drift. + +## Regression Coverage + +- `/Users/wuyongnaren/Documents/印度占星/tests/test_yoga_benchmark_cases.py` +- `/Users/wuyongnaren/Documents/印度占星/tests/test_yoga_rules_integrity.py` diff --git a/references/skill_gap_truth_registry.json b/references/skill_gap_truth_registry.json index d02a88e8..5afcdebb 100644 --- a/references/skill_gap_truth_registry.json +++ b/references/skill_gap_truth_registry.json @@ -132,7 +132,7 @@ "id": "covered_is_not_complete", "wrong_pattern": "Using covered and complete as if they meant the same thing.", "corrected_truth": "Covered means there is a stable entry or usable layer; complete requires mature workflow, tests, boundary text and, where needed, external oracle closure.", - "source_ref": "docs/research/current_skill_core_gap_rerank_2026_06_26.md" + "source_ref": "docs/research/local_drafts/2026-06/current_skill_core_gap_rerank_2026_06_26.md" }, { "id": "single_factor_reading_risk", @@ -150,7 +150,7 @@ "id": "jaimini_kp_tajika_not_absent", "wrong_pattern": "Describing Chara Dasha, KP/Prashna or Tajika/Sahams as absent.", "corrected_truth": "These fronts have usable structures; the hard work is tail-difference closure, traditional workflow deepening and external sample validation.", - "source_ref": "docs/research/three_fronts_skill_depth_audit_2026_06_26.md" + "source_ref": "docs/research/local_drafts/2026-06/three_fronts_skill_depth_audit_2026_06_26.md" } ], "must_not_overclaim": [ diff --git a/scripts/prepare_oracle_capture_packets.py b/scripts/prepare_oracle_capture_packets.py index 57a66b78..1e219e68 100644 --- a/scripts/prepare_oracle_capture_packets.py +++ b/scripts/prepare_oracle_capture_packets.py @@ -19,9 +19,6 @@ from oracle_collection_queue import build_queue, write_evidence_packets # noqa: from oracle_evidence_validator import build_report # noqa: E402 -FIRST_PRIORITY_CAPTURE_ID = "external_template_steve_jobs_dasha_lahiri" - - def _resolve(path: str) -> Path: candidate = Path(path) if candidate.is_absolute(): @@ -41,6 +38,26 @@ def _write_json(path: Path, value: dict[str, Any]) -> None: fh.write("\n") +def _first_priority_packet(queue: dict[str, Any], validator_report: dict[str, Any], output_path: Path) -> Path: + by_capture_id = { + packet.get("capture_id"): packet + for task in queue.get("tasks", []) + if isinstance((packet := task.get("evidence_packet", {})), dict) + } + for packet_result in validator_report.get("packets", []): + if packet_result.get("valid") is True: + continue + capture_id = packet_result.get("capture_id") + if not capture_id or capture_id not in by_capture_id: + continue + return output_path / f"{capture_id}.json" + + packets = sorted(path for path in output_path.glob("external_*.json")) + if not packets: + return output_path / "external_oracle_packet.json" + return packets[0] + + def _write_next_steps(path: Path, oracle_file: str, output_dir: str, first_packet: str) -> None: text = f"""# External Oracle Capture Next Steps @@ -53,8 +70,8 @@ First priority packet: 1. Open JHora, PyJHora, VedAstro, or another documented external source. 2. Use the exact birth data, ayanamsa, node mode, timezone, and settings in the packet. 3. Save a redacted screenshot or stdout snippet under `references/oracle/artifacts/`. -4. Fill all metadata fields and all `target_placeholders`. -5. Set `status` to `external_verified` only after the artifact and target values are filled. +4. Fill missing metadata fields and missing `target_placeholders`. +5. Set or keep `status` as `external_verified` only after the artifact and target values are filled. 不得把本仓库本地输出当作 external oracle。 @@ -78,7 +95,7 @@ python3 scripts/oracle_evidence_validator.py \\ --queue-file /tmp/jyotish_oracle_queue_filled.json ``` -Draft packets in `{output_dir}` must remain `valid_packets: 0` until real external evidence is filled. +Packets in `{output_dir}` must not become valid until real external evidence is filled. """ path.write_text(text, encoding="utf-8") @@ -92,8 +109,7 @@ def prepare_packets(oracle_file: str, output_dir: str) -> dict[str, Any]: validator_report = build_report(queue) packets = sorted(path.name for path in output_path.glob("external_*.json")) - first_priority = output_path / f"{FIRST_PRIORITY_CAPTURE_ID}.json" - first_priority_path = first_priority if first_priority.exists() else output_path / packets[0] + first_priority_path = _first_priority_packet(queue, validator_report, output_path) manifest = { "scope": "external_oracle_capture_packet_manifest", diff --git a/tests/test_oracle_closure_master_dashboard.py b/tests/test_oracle_closure_master_dashboard.py index 6167ccf1..a1cc6681 100644 --- a/tests/test_oracle_closure_master_dashboard.py +++ b/tests/test_oracle_closure_master_dashboard.py @@ -48,12 +48,12 @@ def test_oracle_closure_master_dashboard_aggregates_all_hard_fronts() -> None: assert report["fronts"]["dasha"]["external_verified_tasks"] == 3 assert report["fronts"]["dasha"]["first_priority"] is None assert report["fronts"]["shadbala"]["external_verified_tasks"] == 4 - assert report["fronts"]["shadbala"]["first_priority"]["case_id"] == "template_redacted_place_shadbala_raman" + assert report["fronts"]["shadbala"]["open_tasks"] == 0 + assert report["fronts"]["shadbala"]["first_priority"] is None assert report["fronts"]["tajika_sahams"]["external_verified_tasks"] == 1 assert report["fronts"]["tajika_sahams"]["open_tasks"] == 4 assert report["fronts"]["tajika_sahams"]["first_priority"]["case_id"] == "template_einstein_varshaphala_1905_lahiri" assert report["fronts"]["tajika_sahams"]["first_priority"]["missing_groups"]["target"]["count"] == 10 - assert report["fronts"]["shadbala"]["first_priority"]["manual_fill_plan"]["manual_entry_count"] == 55 assert report["next_action_order"][0]["front"] == "tajika_sahams" assert len(report["next_action_order"]) == 1 @@ -69,7 +69,7 @@ def test_oracle_closure_master_dashboard_markdown_can_be_written(tmp_path: Path) assert "total_tasks: `12`" in markdown assert "can_claim_global_oracle_closure: `false`" in markdown assert "`dasha` | 3 | 3 | `complete`" in markdown - assert "template_redacted_place_shadbala_raman" in markdown + assert "`shadbala` | 4 | 4 | `complete`" in markdown assert "template_einstein_varshaphala_1905_lahiri" in markdown assert "manual entries" in markdown assert "metadata missing" in markdown diff --git a/tests/test_prepare_oracle_capture_packets.py b/tests/test_prepare_oracle_capture_packets.py index 80e315a3..e2eff849 100644 --- a/tests/test_prepare_oracle_capture_packets.py +++ b/tests/test_prepare_oracle_capture_packets.py @@ -32,12 +32,12 @@ def test_prepare_oracle_capture_packets_writes_packets_manifest_and_next_steps(t assert completed.returncode == 0, completed.stderr or completed.stdout report = json.loads(completed.stdout) assert report["scope"] == "external_oracle_capture_packet_preparation" - assert report["packet_count"] == 5 - assert report["validator_summary"]["valid_packets"] == 0 - assert report["validator_summary"]["ready_for_calibration"] == 0 - assert report["first_priority_packet"].endswith("external_template_steve_jobs_dasha_lahiri.json") + assert report["packet_count"] == 6 + assert report["validator_summary"]["valid_packets"] == 4 + assert report["validator_summary"]["ready_for_calibration"] == 4 + assert report["first_priority_packet"].endswith("external_template_historical_epoch_lahiri.json") - packet = tmp_path / "external_template_steve_jobs_dasha_lahiri.json" + packet = tmp_path / "external_template_historical_epoch_lahiri.json" manifest = tmp_path / "capture_manifest.json" next_steps = tmp_path / "OPERATOR_NEXT_STEPS.md" assert packet.exists() @@ -45,17 +45,17 @@ def test_prepare_oracle_capture_packets_writes_packets_manifest_and_next_steps(t assert next_steps.exists() packet_data = json.loads(packet.read_text(encoding="utf-8")) - assert packet_data["status"] == "draft" - assert packet_data["metadata"]["source_artifact"] == "references/oracle/artifacts/" - assert packet_data["target_placeholders"]["target.vimshottari_start_date"] is None + assert packet_data["status"] == "external_verified" + assert packet_data["metadata"]["source_artifact"].startswith("references/oracle/artifacts/") + assert packet_data["target_placeholders"]["target.sun_sidereal_longitude_deg"] is None manifest_data = json.loads(manifest.read_text(encoding="utf-8")) assert manifest_data["scope"] == "external_oracle_capture_packet_manifest" - assert manifest_data["packet_count"] == 5 + assert manifest_data["packet_count"] == 6 assert packet.name in manifest_data["packets"] guide = next_steps.read_text(encoding="utf-8") - assert "external_template_steve_jobs_dasha_lahiri.json" in guide + assert "external_template_historical_epoch_lahiri.json" in guide assert "oracle_evidence_validator.py" in guide assert "--apply-packet" in guide assert "不得把本仓库本地输出当作 external oracle" in guide diff --git a/tests/test_public_benchmark_dashboard.py b/tests/test_public_benchmark_dashboard.py index 8f3c6992..67a62111 100644 --- a/tests/test_public_benchmark_dashboard.py +++ b/tests/test_public_benchmark_dashboard.py @@ -38,7 +38,7 @@ def test_public_benchmark_dashboard_outputs_stable_json_summary() -> None: assert report["schema_version"] == 1 assert report["summary"]["technique_count"] >= 60 assert report["summary"]["capability_valid"] is True - assert report["oracle_readiness"]["total_packets"] == 5 + assert report["oracle_readiness"]["total_packets"] == 6 assert report["oracle_readiness"]["valid_packets"] == 4 assert report["oracle_readiness"]["ready_for_calibration"] == 4 assert report["oracle_readiness"]["production_tuning_allowed"] is False diff --git a/tests/test_yoga_benchmark_cases.py b/tests/test_yoga_benchmark_cases.py new file mode 100644 index 00000000..5990eb39 --- /dev/null +++ b/tests/test_yoga_benchmark_cases.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Executable Yoga benchmark cases recovered from older scratch tests.""" + +from __future__ import annotations + +from scripts.yoga_engine import detect_yogas_from_json + + +RAMAN_STATIC_CHART = { + "ascendant": "Aquarius", + "planets": { + "Sun": {"sign": "Cancer", "house": 6, "degree": 23.0}, + "Moon": {"sign": "Taurus", "house": 4, "degree": 23.5}, + "Mars": {"sign": "Leo", "house": 7, "degree": 21.0}, + "Mercury": {"sign": "Leo", "house": 7, "degree": 14.0}, + "Jupiter": {"sign": "Scorpio", "house": 10, "degree": 13.0}, + "Venus": {"sign": "Leo", "house": 7, "degree": 2.0}, + "Saturn": {"sign": "Taurus", "house": 4, "degree": 10.0}, + "Rahu": {"sign": "Pisces", "house": 2, "degree": 25.0}, + "Ketu": {"sign": "Virgo", "house": 8, "degree": 25.0}, + }, + "context": { + "d9": { + "ascendant": "Aries", + } + }, +} + + +def test_raman_static_chart_triggers_stable_yoga_rule_ids() -> None: + detected = detect_yogas_from_json(RAMAN_STATIC_CHART) + rule_ids = {row.get("rule_id") for row in detected} + + assert len(detected) >= 50 + assert { + "dharma_karmadhipati", + "chandra_yoga_exalted", + "shakti_yoga_9_10_lord", + "bvr_016_vesi_precise", + "budha_shukra_yoga", + "kalatra_yoga_venus_kendra", + "raja_yoga_5_10_lord", + "raja_yoga_4_10_lord", + }.issubset(rule_ids) + + +def test_raman_static_chart_preserves_yoga_metadata_contract() -> None: + detected = detect_yogas_from_json(RAMAN_STATIC_CHART) + by_rule_id = {row.get("rule_id"): row for row in detected} + + dharma = by_rule_id["dharma_karmadhipati"] + assert dharma["category"] == "raja" + assert dharma["strength"] == "强" + assert "9宫主金星Venus" in dharma["combination"] + assert "10宫主火星Mars" in dharma["combination"] + + chandra = by_rule_id["chandra_yoga_exalted"] + assert chandra["category"] == "chandra" + assert chandra["strength"] == "强" + assert chandra["source"] == "BPHS"