feat: add rectification input evidence contract

This commit is contained in:
732642856
2026-07-21 22:44:59 +08:00
parent 8df4a74fec
commit 31dc3a9b97
11 changed files with 261 additions and 16 deletions
@@ -25,3 +25,6 @@ def test_scanner_reports_real_divisional_transitions(monkeypatch):
assert report["transitions"]
assert report["pending_layers"] == ["UL", "A7", "A10", "KP_cusp"]
assert report["rows"][0]["divisional_ascendants"]["D9"] in {"Aries", "Taurus"}
assert report["input_contract"]["center_input_fingerprint"]
assert report["rows"][0]["input_fingerprint"] != report["rows"][1]["input_fingerprint"]
assert report["stability_contract"]["minute_confirmation_allowed"] is False
@@ -1,21 +1,38 @@
import json
import subprocess
from pathlib import Path
from scripts import jyotishganit_vs_local_field_comparison as comparison
ROOT = Path(__file__).resolve().parents[1]
INDEX = ROOT / "references/oracle/evidence_packet_index_2026_07_19.json"
def test_jyotishganit_vs_local_comparison_outputs_sign_rows_and_hash():
out = subprocess.check_output(["python3", "scripts/jyotishganit_vs_local_field_comparison.py"], cwd=ROOT, text=True)
data = json.loads(out)
def test_jyotishganit_vs_local_comparison_outputs_sign_rows_and_hash(monkeypatch):
def fake_local(_):
return {code: {"Sun": {"sign": "Aries"}, "Moon": {"sign": "Taurus"}} for code in comparison.TARGETS["D2"] + comparison.TARGETS["D4"] + comparison.TARGETS["D9"] + comparison.TARGETS["D10"]}
def fake_jyotishganit(_):
return {
"divisionalCharts": {
code.lower(): {"houses": [{"occupants": [{"celestialBody": "Sun", "sign": "Aries"}, {"celestialBody": "Moon", "sign": "Taurus"}]}]}
for code in comparison.TARGETS
},
"panchanga": {},
"ashtakavarga": {"sav": {}},
}
monkeypatch.setattr(comparison, "local_varga", fake_local)
monkeypatch.setattr(comparison, "jyotishganit_raw", fake_jyotishganit)
data = comparison.build(comparison.build_parser().parse_args([]))
assert data["scope"] == "jyotishganit_vs_local_field_comparison"
assert data["claim_status"] == "observation_only"
assert data["production_tuning_allowed"] is False
assert data["truth_matrix_allowed"] is False
assert data["summary"]["row_count"] >= 32
assert data["summary"]["row_count"] == 8
assert data["comparison_hash"]
assert data["request"]["latitude"] == 37.7749
assert data["request"]["longitude"] == -122.4194
assert {row["section"] for row in data["rows"]} == {"D2", "D4", "D9", "D10"}
assert data["coverage"]["panchanga_jyotishganit"] is True
assert data["coverage"]["BAV_SAV_jyotishganit"] is True
@@ -0,0 +1,55 @@
from scripts.rectification_input_contract import (
candidate_input_fingerprint,
canonical_birth_input,
semantic_evidence_hash,
stability_probe_contract,
)
CASE = {
"year": 1955,
"month": 2,
"day": 24,
"hour": 19,
"minute": 15,
"lat": 37.7749,
"lon": -122.4194,
"tz": -8,
}
def test_canonical_birth_input_is_order_independent_and_explicit_about_settings():
reordered = {key: CASE[key] for key in reversed(CASE)}
first = canonical_birth_input(CASE)
second = canonical_birth_input(reordered)
assert first == second
assert first["ayanamsa"] == "lahiri"
assert first["node_mode"] == "true"
assert candidate_input_fingerprint(CASE) == candidate_input_fingerprint(reordered)
def test_candidate_fingerprint_changes_when_only_the_minute_changes():
next_minute = {**CASE, "minute": 16}
assert candidate_input_fingerprint(CASE) != candidate_input_fingerprint(next_minute)
def test_stability_probes_are_explicit_but_do_not_claim_minute_confirmation():
contract = stability_probe_contract(CASE)
assert contract["status"] == "pending_score_comparison"
assert [probe["offset_minutes"] for probe in contract["probes"]] == [-5, -2, -1, 1, 2, 5]
assert contract["minute_confirmation_allowed"] is False
assert contract["blocker"] == "public_blind_minute_holdout_not_closed"
assert all("input_fingerprint" in probe for probe in contract["probes"])
def test_semantic_evidence_hash_ignores_known_order_insensitive_aspect_lists_only():
left = {"aspects": {"gives": ["Mars", "Saturn"], "receives": ["Moon", "Sun"]}, "value": [2, 1]}
right = {"value": [2, 1], "aspects": {"receives": ["Sun", "Moon"], "gives": ["Saturn", "Mars"]}}
changed = {"value": [1, 2], "aspects": {"receives": ["Sun", "Moon"], "gives": ["Saturn", "Mars"]}}
assert semantic_evidence_hash(left) == semantic_evidence_hash(right)
assert semantic_evidence_hash(left) != semantic_evidence_hash(changed)
@@ -8,6 +8,22 @@ SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
if str(SCRIPTS) not in sys.path:
sys.path.insert(0, str(SCRIPTS))
from rectification_three_engine_packet import build_packet, case_hash
CASE = {"year": 1990, "month": 1, "day": 1, "hour": 12, "minute": 0, "lat": 0.0, "lon": 0.0, "tz": 0.0}
def test_packet_is_private_and_never_confirms(monkeypatch) -> None:
import rectification_three_engine_packet as module
monkeypatch.setattr(module, "_local_d1", lambda _: {"Sun": "Aries"})
monkeypatch.setattr(module, "_pyjhora_d1", lambda _: {"Sun": "Aries"})
monkeypatch.setattr(module, "_jyotishganit_d1", lambda _: {"Sun": "Aries"})
packet = build_packet(CASE)
assert packet["case_hash"] == case_hash(CASE)
assert packet["input_contract_hash"]
assert packet["stability_contract"]["minute_confirmation_allowed"] is False
assert "year" not in str(packet)
assert packet["can_confirm"] is False
assert packet["vedastro"]["status"] == "requires_gateway_raw_archive"
def test_packet_queues_a_privacy_safe_vedastro_receipt(monkeypatch) -> None:
import rectification_three_engine_packet as packet