From e15fe3a5456f31c6c91761e2a56d91b220bc2fa2 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 28 Jun 2026 17:01:56 +0800 Subject: [PATCH] Harden Shadbala oracle and VedAstro range evidence --- docs/research/ACTIVE_FRONTS.md | 3 + ...lute_oracle_comparison_audit_2026_06_28.md | 52 +++++++++++++ ...o_range_scan_allowlist_audit_2026_06_28.md | 48 ++++++++++++ scripts/shadbala_oracle_comparison.py | 5 +- scripts/vedastro_service_adapter.py | 58 ++++++++++++++- .../test_vedastro_service_adapter_executor.py | 74 +++++++++++++++++++ 6 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 docs/research/shadbala_absolute_oracle_comparison_audit_2026_06_28.md create mode 100644 docs/research/vedastro_range_scan_allowlist_audit_2026_06_28.md diff --git a/docs/research/ACTIVE_FRONTS.md b/docs/research/ACTIVE_FRONTS.md index 9ec22d72..7ed728ac 100644 --- a/docs/research/ACTIVE_FRONTS.md +++ b/docs/research/ACTIVE_FRONTS.md @@ -34,6 +34,8 @@ This file is the small index for the current engineering fronts that still drive - `/Users/wuyongnaren/Documents/印度占星/docs/research/oracle_benchmark_inventory_latest.json` - `/Users/wuyongnaren/Documents/印度占星/docs/research/oracle_closure_master_dashboard_latest.md` - `/Users/wuyongnaren/Documents/印度占星/docs/research/public_benchmark_dashboard_latest.md` +- `/Users/wuyongnaren/Documents/印度占星/docs/research/shadbala_absolute_oracle_comparison_audit_2026_06_28.md` +- `/Users/wuyongnaren/Documents/印度占星/scripts/shadbala_oracle_comparison.py` - `/Users/wuyongnaren/Documents/印度占星/references/oracle/` - Before changing oracle-dependent adjudicator or benchmark claims, run: - `python3 scripts/oracle_benchmark_inventory.py --format json` @@ -43,6 +45,7 @@ This file is the small index for the current engineering fronts that still drive - `/Users/wuyongnaren/Documents/印度占星/docs/research/vedastro_parity_matrix_latest.md` - `/Users/wuyongnaren/Documents/印度占星/docs/research/vedastro_parity_matrix_latest.json` - `/Users/wuyongnaren/Documents/印度占星/docs/research/life_event_graph_v1_audit_2026_06_28.md` +- `/Users/wuyongnaren/Documents/印度占星/docs/research/vedastro_range_scan_allowlist_audit_2026_06_28.md` - `/Users/wuyongnaren/Documents/印度占星/scripts/vedastro_service_adapter.py` - follow only after the Jaimini marriage bridge v1 regression loop is closed - Use the parity matrix before adding or claiming VedAstro-equivalent capability. diff --git a/docs/research/shadbala_absolute_oracle_comparison_audit_2026_06_28.md b/docs/research/shadbala_absolute_oracle_comparison_audit_2026_06_28.md new file mode 100644 index 00000000..145af185 --- /dev/null +++ b/docs/research/shadbala_absolute_oracle_comparison_audit_2026_06_28.md @@ -0,0 +1,52 @@ +# Shadbala Absolute Oracle Comparison Audit - 2026-06-28 + +## Scope + +This pass promotes the local Shadbala absolute-value comparison helper from an untracked fragment into a formal oracle-closure asset. + +## New Entrypoint + +- `/Users/wuyongnaren/Documents/印度占星/scripts/shadbala_oracle_comparison.py` +- Primary function: `compare_case(oracle_file, case_id)` +- CLI example: + +```bash +python3 scripts/shadbala_oracle_comparison.py \ + --oracle-file references/oracle/dasha_shadbala_oracle_cases.json \ + --case-id template_steve_jobs_dasha_lahiri \ + --format json +``` + +## Output Contract + +The report exposes: + +- `scope = shadbala_absolute_oracle_comparison` +- `case_id` +- external oracle `status` +- birth and settings metadata +- per-planet `oracle_total_rupa`, `local_total_rupa`, and `diff_total_rupa` +- per-component Rupa deltas +- `global_scaling_check` +- tolerance metadata + +## Boundary + +- This is diagnostic oracle evidence. +- It does not authorize global Shadbala scaling. +- It does not tune constants from a single case. +- It preserves component-level deltas so future fixes can target the actual failing sub-strength instead of hiding mismatch inside a total score. + +## Current External-Verified Case Snapshot + +For `template_steve_jobs_dasha_lahiri`: + +- `planet_count = 7` +- `planets_within_total_tolerance = 5` +- `max_abs_total_delta_rupa = 2.085` + +This is useful enough to keep in the main repo, but not sufficient to claim Shadbala oracle closure. + +## Regression Coverage + +- `/Users/wuyongnaren/Documents/印度占星/tests/test_shadbala_oracle_comparison.py` diff --git a/docs/research/vedastro_range_scan_allowlist_audit_2026_06_28.md b/docs/research/vedastro_range_scan_allowlist_audit_2026_06_28.md new file mode 100644 index 00000000..560d4283 --- /dev/null +++ b/docs/research/vedastro_range_scan_allowlist_audit_2026_06_28.md @@ -0,0 +1,48 @@ +# VedAstro Range Scan Allowlist Audit - 2026-06-28 + +## Scope + +This pass hardens the VedAstro range-scan adapter so external `EventsAtRange`-style responses do not enter the local evidence ledger unfiltered. + +## Behavior + +`scripts/vedastro_service_adapter.py` now filters range-scan events through a per-domain allowlist before writing `evidence_ledger`. + +Supported domains: + +- `marriage` +- `wealth` +- `career` + +Each domain can accept an event by either: + +- known event id, or +- at least one relevant tag. + +## Boundary + +- The allowlist only filters external candidate evidence. +- It does not promote VedAstro events into final conclusions. +- It does not change local Dasha, Narayana, Jaimini, Varga, Shadbala, Ashtakavarga, or strict workflow scoring. +- External range evidence remains secondary/oracle evidence until a domain adjudicator explicitly promotes it through tests. + +## Contract Exposure + +`--print-schema` now exposes: + +```json +{ + "range_scan_event_allowlist": { + "marriage": { + "event_ids": [], + "tags": [] + } + } +} +``` + +This prevents future agents from treating the filtering layer as hidden behavior. + +## Regression Coverage + +- `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_service_adapter_executor.py` diff --git a/scripts/shadbala_oracle_comparison.py b/scripts/shadbala_oracle_comparison.py index 03c6f0e9..41fa4e73 100644 --- a/scripts/shadbala_oracle_comparison.py +++ b/scripts/shadbala_oracle_comparison.py @@ -8,7 +8,10 @@ import json from pathlib import Path from typing import Any -import oracle_boundary_audit +try: + from scripts import oracle_boundary_audit +except ImportError: # pragma: no cover - supports direct script execution. + import oracle_boundary_audit ROOT = Path(__file__).resolve().parents[1] diff --git a/scripts/vedastro_service_adapter.py b/scripts/vedastro_service_adapter.py index 261f19f8..582719fb 100644 --- a/scripts/vedastro_service_adapter.py +++ b/scripts/vedastro_service_adapter.py @@ -60,6 +60,36 @@ PARITY_CASES = { } SUPPORTED_RANGE_SCAN_DOMAINS = {"marriage", "wealth", "career"} +RANGE_SCAN_EVENT_ALLOWLIST = { + "marriage": { + "event_ids": { + "GocharJupiterIn7th", + "GocharJupiterAspect7th", + "GocharSaturnAspect7th", + "JupiterSupportsMarriageAxis", + }, + "tags": {"marriage", "relationship", "spouse", "transit"}, + }, + "wealth": { + "event_ids": { + "GocharJupiterIn2nd", + "GocharJupiterIn11th", + "GocharJupiterAspect2nd", + "GocharJupiterAspect11th", + "WealthExpansionWindow", + }, + "tags": {"wealth", "finance", "income", "gains", "transit"}, + }, + "career": { + "event_ids": { + "GocharJupiterIn10th", + "GocharSaturnIn10th", + "GocharJupiterAspect10th", + "CareerExpansionWindow", + }, + "tags": {"career", "profession", "work", "transit"}, + }, +} DEFAULT_TIMEOUT_SECONDS = 8 TIMEOUT_ENV = "VEDASTRO_TIMEOUT_SECONDS" RETRY_POLICY = { @@ -85,6 +115,13 @@ def schema() -> dict[str, Any]: **PARITY_CASES["beijing_first_use_demo"], "body_list": ["Sun", "Moon", "Ascendant", "Rahu", "Ketu"], } + range_scan_allowlist = { + domain: { + "event_ids": sorted(values["event_ids"]), + "tags": sorted(values["tags"]), + } + for domain, values in sorted(RANGE_SCAN_EVENT_ALLOWLIST.items()) + } return { "adapter": "vedastro_service_adapter", "backend": "vedastro_service_adapter_candidate", @@ -144,6 +181,7 @@ def schema() -> dict[str, Any]: "evidence_ledger", "source_metadata", ], + "range_scan_event_allowlist": range_scan_allowlist, "request_example": request_example, "provenance_contract": { "external_service": True, @@ -222,20 +260,32 @@ def _normalize_range_scan_success( if not isinstance(events, list): events = [] + domain = request_preview["domain"] + allowlist = RANGE_SCAN_EVENT_ALLOWLIST.get(domain, {}) + allowed_ids = allowlist.get("event_ids", set()) + allowed_tags = allowlist.get("tags", set()) + evidence_ledger = [] for index, event in enumerate(events, start=1): if not isinstance(event, dict): continue + event_id = event.get("id") or event.get("name") or f"event_{index}" + tags = event.get("tags") or [] + if not isinstance(tags, list): + tags = [] + tag_set = {str(tag) for tag in tags} + if event_id not in allowed_ids and tag_set.isdisjoint(allowed_tags): + continue evidence_ledger.append( { "source": "vedastro_service_adapter_candidate", "operation": "range_scan", - "domain": request_preview["domain"], - "event_id": event.get("id") or event.get("name") or f"event_{index}", + "domain": domain, + "event_id": event_id, "start": event.get("start") or event.get("start_time") or event.get("start_date"), "end": event.get("end") or event.get("end_time") or event.get("end_date"), "score": event.get("score") if event.get("score") is not None else event.get("strength"), - "tags": event.get("tags") or [], + "tags": tags, "raw": event, } ) @@ -259,7 +309,7 @@ def _normalize_range_scan_success( "available": True, "status": "ok", "operation": "range_scan", - "domain": request_preview["domain"], + "domain": domain, "request_preview": request_preview, "event_count": len(evidence_ledger), "top_event": top_event, diff --git a/tests/test_vedastro_service_adapter_executor.py b/tests/test_vedastro_service_adapter_executor.py index c6a09893..acf486d2 100644 --- a/tests/test_vedastro_service_adapter_executor.py +++ b/tests/test_vedastro_service_adapter_executor.py @@ -41,6 +41,8 @@ def test_vedastro_service_adapter_executor_schema_is_declared() -> None: assert "start_date" in report["range_scan_request_contract"] assert "range_scan_response_contract" in report assert "evidence_ledger" in report["range_scan_response_contract"] + assert report["range_scan_event_allowlist"]["marriage"]["event_ids"] + assert "marriage" in report["range_scan_event_allowlist"]["marriage"]["tags"] def test_vedastro_service_adapter_returns_controlled_unconfigured_status() -> None: @@ -295,6 +297,78 @@ def test_vedastro_service_adapter_can_normalize_mock_range_scan_response() -> No assert report["source_metadata"]["endpoint"].startswith("http://127.0.0.1:") +def test_vedastro_service_adapter_applies_domain_allowlist_to_range_scan_noise() -> None: + class Handler(BaseHTTPRequestHandler): + def do_POST(self) -> None: # noqa: N802 + response = { + "events": [ + { + "id": "GocharJupiterIn7th", + "name": "Jupiter enters 7th house", + "start": "2026-05-01", + "end": "2026-06-01", + "score": 72, + "tags": ["marriage", "transit"], + }, + { + "id": "RandomMoonMoodShift", + "name": "Random moon mood shift", + "start": "2026-05-08", + "end": "2026-05-09", + "score": 91, + "tags": ["emotion", "noise"], + }, + ] + } + body = json.dumps(response).encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def log_message(self, format: str, *args) -> None: # noqa: A003 + return + + server = HTTPServer(("127.0.0.1", 0), Handler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + env = os.environ.copy() + env["VEDASTRO_API_ENDPOINT"] = f"http://127.0.0.1:{server.server_port}/vedastro" + env["VEDASTRO_ENABLE_NETWORK"] = "1" + completed = subprocess.run( + [ + sys.executable, + "scripts/vedastro_service_adapter.py", + "--range-scan", + "--domain", + "marriage", + "--case", + "beijing_first_use_demo", + "--start-date", + "2026-01-01", + "--end-date", + "2031-01-01", + ], + cwd=ROOT, + text=True, + capture_output=True, + timeout=120, + check=False, + env=env, + ) + finally: + server.shutdown() + thread.join(timeout=5) + + assert completed.returncode == 0, completed.stderr or completed.stdout + report = json.loads(completed.stdout) + assert report["event_count"] == 1 + assert report["top_event"]["event_id"] == "GocharJupiterIn7th" + assert report["evidence_ledger"][0]["event_id"] == "GocharJupiterIn7th" + + def test_vedastro_service_adapter_classifies_http_error() -> None: class Handler(BaseHTTPRequestHandler): def do_POST(self) -> None: # noqa: N802