diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index a3cd6569..e7e5d79c 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -57,6 +57,7 @@ For large architecture or release work, also read: | ERR-024 | VedAstro gateway status can choose `local_fallback` before `.env` is loaded, even when official endpoint/network settings are present. | mitigated 2026-07-08 | `gateway_status()` must load official readiness before resolving active backend; `run_gateway_packet()` must expose `official_closure_state` separately from legacy `status`. | | ERR-025 | VedAstro gateway can report legacy `status=ok` from catalog availability even when no official raw response is present. | mitigated 2026-07-08 | `official_closure_state=official_verified` requires `official_raw_response`; otherwise expose `official_closure_reason=official_raw_response_missing`. | | ERR-026 | VedAstro service adapter can obtain an official full-snapshot raw response while the user entrypoint drops it, leaving gateway official closure permanently blocked. | mitigated 2026-07-09 | `vedastro_user_entrypoint` must expose `vedastro_official_full_snapshot.raw_response_available` and root `official_raw_response` when explicitly requested; gateway tests must prove raw propagation reaches `official_verified`. | +| ERR-027 | External engine readiness diagnostics can be mistaken for a completed same-chart parity comparison. | mitigated 2026-07-09 | `diagnose_external_engine_adapters.py` must expose `same_chart_parity_contract.required_outputs`, per-engine expected oracle fields, and `tested=false` until a real same-chart comparison runs. | ## Fragment Sweep Command Set diff --git a/scripts/diagnose_external_engine_adapters.py b/scripts/diagnose_external_engine_adapters.py index 7ee0ef05..1b6284bc 100644 --- a/scripts/diagnose_external_engine_adapters.py +++ b/scripts/diagnose_external_engine_adapters.py @@ -16,6 +16,50 @@ except Exception: # pragma: no cover - import path varies in tests/CLI from scripts.diagnose_jyotishganit_adapter import build_report as build_jyotishganit_report +REQUIRED_PARITY_OUTPUTS = ["D1", "D9", "D10", "D2", "D4", "Vimshottari", "Shadbala", "Ashtakavarga"] + + +def _same_chart_parity_contract(engines: dict) -> dict: + engine_states = {} + for name, engine in engines.items(): + available = engine["status"] == "available" + engine_states[name] = { + "available": available, + "tested": False, + "blocked": not available, + "blocking_reason": "" if available else engine["status"], + } + return { + "status": "ready" if all(state["available"] for state in engine_states.values()) else "blocked", + "required_outputs": REQUIRED_PARITY_OUTPUTS, + "expected_oracle_fields": { + "VedAstro": [ + "official_raw_response", + "official_chart", + "section_statuses", + "request_manifest", + "source_metadata.artifact_path", + ], + "PyJHora/JHora": [ + "raw_output_path", + "settings.ayanamsa", + "settings.node_mode", + "D1", + "D9", + "D10", + "D2", + "D4", + "Vimshottari", + "Shadbala", + "Ashtakavarga", + ], + "jyotishganit": ["raw_output_path", "panchanga", "tithi", "nakshatra", "yoga", "karana"], + }, + "engine_states": engine_states, + "boundary": "This is a parity contract, not proof that the same-chart comparison has run.", + } + + def build_report() -> dict: vedastro = build_vedastro_report() pyjhora = build_pyjhora_report() @@ -45,6 +89,7 @@ def build_report() -> dict: "scope": "external_engine_adapter_diagnostics", "status": "complete" if all(engine["status"] == "available" for engine in engines.values()) else "partial", "engines": engines, + "same_chart_parity_contract": _same_chart_parity_contract(engines), "boundary": "Readiness diagnostics only; this does not run a three-engine consultation comparison.", } diff --git a/tests/test_external_engine_adapter_diagnostics.py b/tests/test_external_engine_adapter_diagnostics.py index e98df1fa..a5e48e81 100644 --- a/tests/test_external_engine_adapter_diagnostics.py +++ b/tests/test_external_engine_adapter_diagnostics.py @@ -30,4 +30,11 @@ def test_external_engine_adapter_diagnostics_aggregates_three_engines() -> None: assert report["engines"]["PyJHora/JHora"]["install_hint"]["package"] == "PyJHora" assert report["engines"]["PyJHora/JHora"]["license_boundary"].startswith("AGPL external benchmark") assert report["engines"]["jyotishganit"]["license"] == "MIT" + contract = report["same_chart_parity_contract"] + assert contract["status"] in {"ready", "blocked"} + assert contract["required_outputs"] == ["D1", "D9", "D10", "D2", "D4", "Vimshottari", "Shadbala", "Ashtakavarga"] + assert "official_raw_response" in contract["expected_oracle_fields"]["VedAstro"] + assert "raw_output_path" in contract["expected_oracle_fields"]["PyJHora/JHora"] + assert contract["engine_states"]["jyotishganit"]["available"] is True + assert contract["engine_states"]["PyJHora/JHora"]["tested"] is False assert report["status"] in {"complete", "partial"}