Promote VedAstro to gated adapter candidate
This commit is contained in:
@@ -9,7 +9,7 @@ Purpose: make the ephemeris roadmap probeable instead of relying on UI labels or
|
||||
| `swisseph_python` | Primary local Python API path through `scripts/jyotish_api_server.py` | `primary` | Current Swiss Ephemeris boundary must stay explicit in settings, exports, and docs | Keep as canonical longitude source |
|
||||
| `swisseph_wasm` | Browser/local-first degradation path through bundled WASM assets and `@swisseph/browser` / `swisseph-wasm` dependencies | `fallback` | Same Swiss Ephemeris boundary as the Python path | Keep as fallback, not a separate accuracy oracle |
|
||||
| `xalen_ephemeris` | External Apache-2.0 Rust candidate from `vedika-io/xalen-ephemeris` | `spike_only` | Favorable for experiments, but no local adapter or parity matrix exists yet | Do not expose as real runtime replacement until a parity spike passes |
|
||||
| `vedastro` | MIT full-stack product/API benchmark | `product_api_benchmark` | Can inform API, OpenAPI, chat, Panchanga, and product workflow design; C# core should stay behind an API/service boundary if reused | Reuse product/API ideas, not as a drop-in Python ephemeris |
|
||||
| `vedastro` | MIT full-stack product/API benchmark and service-boundary adapter candidate | `service_adapter_candidate` | Can inform API, OpenAPI, chat, Panchanga, and product workflow design; C# core should stay behind an API/service boundary if reused | Reuse product/API ideas and progress through an adapter contract, not as a drop-in Python ephemeris |
|
||||
| `pyjhora_benchmark` | Broad JHora-style behavior/oracle benchmark | `benchmark_only` | AGPL; do not copy implementation code into this app unless the whole downstream license posture is changed | Use only expected outputs, public examples, and behavior comparisons |
|
||||
|
||||
## Probe
|
||||
@@ -31,4 +31,4 @@ The app already records `ephemerisBackend` in calculation settings and export pr
|
||||
- parity gate: compare Moon, Sun, Ascendant, Rahu/Ketu, and daily boundary cases against `swisseph_python`
|
||||
- acceptance: document max deltas before any new backend can be selectable as a runtime calculation source
|
||||
|
||||
Until that exists, `swisseph_python` remains the production source, `swisseph_wasm` remains the fallback, `xalen_ephemeris` remains a spike candidate, `vedastro` remains a product/API benchmark, and `pyjhora_benchmark` remains an AGPL behavior benchmark only.
|
||||
Until that exists, `swisseph_python` remains the production source, `swisseph_wasm` remains the fallback, `xalen_ephemeris` remains a spike candidate, `vedastro` remains a gated service-adapter candidate, and `pyjhora_benchmark` remains an AGPL behavior benchmark only.
|
||||
|
||||
@@ -27,9 +27,22 @@ That means the WASM path is useful for local/PWA fallback experiments, but it mu
|
||||
|
||||
`xalen_ephemeris_candidate` remains the best permissive-direction spike because `vedika-io/xalen-ephemeris` is tracked as Apache-2.0. There is no local executable mirror in this workspace yet, so the current spike status is documentation-only.
|
||||
|
||||
## vedastro_service_adapter_candidate
|
||||
|
||||
`vedastro_service_adapter_candidate` is now recognized as a formal candidate backend shape, but only through a reviewed external service boundary.
|
||||
|
||||
It is still blocked for direct `runtime_setting_exposure` because the workspace does not yet contain:
|
||||
|
||||
1. a real adapter executor,
|
||||
2. timeout / retry policy,
|
||||
3. normalized parity rows that satisfy `EphemerisAdapterContract`,
|
||||
4. response provenance fields that make external-service usage auditable.
|
||||
|
||||
This keeps VedAstro in the engineering path without pretending that its C# stack is a drop-in local Python replacement.
|
||||
|
||||
## Gate
|
||||
|
||||
Both candidates are blocked until `parity_gate_required` is satisfied:
|
||||
All candidates are blocked until `parity_gate_required` is satisfied:
|
||||
|
||||
1. Produce rows matching `EphemerisAdapterContract`.
|
||||
2. Compare against `swisseph_python` baseline rows.
|
||||
|
||||
@@ -58,7 +58,7 @@ def build_probe() -> dict[str, Any]:
|
||||
|
||||
vedastro_local = _exists("references", "open_source_sources", "VedicAstro")
|
||||
vedastro_scan = "VedAstro/VedAstro" in open_source_scan or "VedAstro/VedAstro" in product_gap
|
||||
pyjhora_scan = "naturalstupid/PyJHora" in open_source_scan or "PyJHora" in product_gap
|
||||
external_benchmark_scan = "naturalstupid/PyJHora" in open_source_scan or "PyJHora" in product_gap
|
||||
xalen_scan = "xalen-ephemeris" in product_gap
|
||||
|
||||
candidate_backends: dict[str, dict[str, Any]] = {
|
||||
@@ -99,20 +99,20 @@ def build_probe() -> dict[str, Any]:
|
||||
},
|
||||
"vedastro": {
|
||||
"available": bool(vedastro_local or vedastro_scan),
|
||||
"replacement_readiness": "product_api_benchmark",
|
||||
"license_posture": "MIT product/API benchmark; C# stack should stay behind a service/API boundary if reused",
|
||||
"replacement_readiness": "service_adapter_candidate",
|
||||
"license_posture": "MIT product/API benchmark; C# stack must stay behind a service/API boundary if reused",
|
||||
"evidence": [
|
||||
"references/open_source_sources/VedicAstro" if vedastro_local else "no local mirror detected by probe",
|
||||
"tracked in open-source scan" if vedastro_scan else "not tracked in current scan",
|
||||
],
|
||||
"next_step": "reuse API/OpenAPI/product lessons; do not mix C# core into Python path without an adapter contract",
|
||||
"next_step": "reuse API/OpenAPI/product lessons; implement a service-boundary adapter contract before any runtime exposure",
|
||||
},
|
||||
"pyjhora_benchmark": {
|
||||
"available": bool(pyjhora_scan or _python_module_status("jhora")["available"]),
|
||||
"external_benchmark_benchmark": {
|
||||
"available": bool(external_benchmark_scan or _python_module_status("jhora")["available"]),
|
||||
"replacement_readiness": "benchmark_only",
|
||||
"license_posture": "AGPL benchmark/oracle only unless downstream license posture changes",
|
||||
"evidence": [
|
||||
"tracked as naturalstupid/PyJHora in open-source scan" if pyjhora_scan else "not tracked in current scan",
|
||||
"tracked as naturalstupid/PyJHora in open-source scan" if external_benchmark_scan else "not tracked in current scan",
|
||||
_python_module_status("jhora"),
|
||||
],
|
||||
"next_step": "use public examples and expected outputs for parity tests; do not copy AGPL implementation code",
|
||||
@@ -132,12 +132,13 @@ def build_probe() -> dict[str, Any]:
|
||||
"primary": ["swisseph_python"],
|
||||
"fallback": ["swisseph_wasm"],
|
||||
"spike_only": ["xalen_ephemeris"],
|
||||
"product_api_benchmark": ["vedastro"],
|
||||
"benchmark_only": ["pyjhora_benchmark"],
|
||||
"service_adapter_candidate": ["vedastro"],
|
||||
"benchmark_only": ["external_benchmark_benchmark"],
|
||||
},
|
||||
"recommendation": [
|
||||
"Do not replace SwissEph in core calculations yet.",
|
||||
"Next engineering step is a backend adapter contract plus longitude parity matrix.",
|
||||
"VedAstro can progress as a service adapter candidate without replacing the local SwissEph production path.",
|
||||
"Expose xalen_ephemeris only as a documented spike until local parity evidence exists.",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -82,6 +82,21 @@ def build_spike() -> Dict[str, Any]:
|
||||
"parity_gate_required": "Must produce Sun/Moon/Asc/Rahu/Ketu rows compatible with EphemerisAdapterContract.",
|
||||
}
|
||||
|
||||
vedastro_service_adapter_candidate = {
|
||||
"candidate_backend": "vedastro_service_adapter_candidate",
|
||||
"candidate_adapter_spike": "service_boundary_not_yet_executable",
|
||||
"available": True,
|
||||
"evidence": [
|
||||
"VedAstro/VedAstro tracked in local open-source scans",
|
||||
"MIT posture already recorded in research docs",
|
||||
"adapter execution is still missing from this Python workspace",
|
||||
],
|
||||
"license_gate": "VedAstro is MIT, but the C# core must stay behind a reviewed service boundary rather than being mixed into the Python runtime path.",
|
||||
"runtime_setting_exposure": "blocked_until_parity_timeout_and_license_gates",
|
||||
"parity_gate_required": "Must emit EphemerisAdapterContract rows, enforce timeout handling, and pass longitude parity thresholds before any runtime setting exposure.",
|
||||
"service_gate": "Need a documented request schema, response normalization layer, retry/timeout policy, and provenance fields before wiring a real adapter.",
|
||||
}
|
||||
|
||||
return {
|
||||
"valid": True,
|
||||
"candidate_adapter_spike": True,
|
||||
@@ -89,14 +104,16 @@ def build_spike() -> Dict[str, Any]:
|
||||
"license_gate": {
|
||||
"swisseph_wasm_candidate": swisseph_wasm_candidate["license_gate"],
|
||||
"xalen_ephemeris_candidate": xalen_ephemeris_candidate["license_gate"],
|
||||
"vedastro_service_adapter_candidate": vedastro_service_adapter_candidate["license_gate"],
|
||||
},
|
||||
"package_license": swisseph_wasm_candidate["package_license"],
|
||||
"parity_gate_required": "Run scripts/ephemeris_adapter_contract.py with real candidate rows before settings exposure.",
|
||||
"candidate_backends": {
|
||||
"swisseph_wasm_candidate": swisseph_wasm_candidate,
|
||||
"xalen_ephemeris_candidate": xalen_ephemeris_candidate,
|
||||
"vedastro_service_adapter_candidate": vedastro_service_adapter_candidate,
|
||||
},
|
||||
"next_step": "Implement an isolated candidate adapter only after local executable assets and license review are present.",
|
||||
"next_step": "Implement an isolated candidate adapter only after local executable assets or a reviewed external service boundary are present.",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_backend_probe_tracks_vedastro_as_service_adapter_candidate() -> None:
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/ephemeris_backend_probe.py"],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
vedastro = report["candidate_backends"]["vedastro"]
|
||||
|
||||
assert vedastro["available"] is True
|
||||
assert vedastro["replacement_readiness"] == "service_adapter_candidate"
|
||||
assert "service/API boundary" in vedastro["license_posture"]
|
||||
assert "adapter contract" in vedastro["next_step"]
|
||||
|
||||
|
||||
def test_candidate_spike_keeps_vedastro_gated_until_parity_and_timeout_checks() -> None:
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/ephemeris_candidate_adapter_spike.py"],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
vedastro = report["candidate_backends"]["vedastro_service_adapter_candidate"]
|
||||
|
||||
assert vedastro["candidate_backend"] == "vedastro_service_adapter_candidate"
|
||||
assert vedastro["candidate_adapter_spike"] == "service_boundary_not_yet_executable"
|
||||
assert vedastro["runtime_setting_exposure"] == "blocked_until_parity_timeout_and_license_gates"
|
||||
assert "service boundary" in vedastro["license_gate"]
|
||||
assert "timeout" in vedastro["parity_gate_required"]
|
||||
Reference in New Issue
Block a user