chore: remove legacy Vite frontend
This commit is contained in:
@@ -21,7 +21,7 @@ from typing import Any
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REGISTRY_PATH = ROOT / "references" / "technique_registry.json"
|
||||
SCRIPTS_DIR = ROOT / "scripts"
|
||||
APP_DIR = ROOT / "jyotish-app"
|
||||
APP_DIR = ROOT / "frontend" / "src"
|
||||
TESTS_DIR = ROOT / "tests"
|
||||
OPEN_SOURCE_DIR = ROOT / "references" / "open_source_sources"
|
||||
|
||||
@@ -135,9 +135,9 @@ def scan_frontend() -> dict[str, Any]:
|
||||
files: list[str] = []
|
||||
if APP_DIR.exists():
|
||||
for path in APP_DIR.rglob("*"):
|
||||
if any(part in {"node_modules", "dist", ".vite"} for part in path.parts):
|
||||
if any(part in {"node_modules", ".next"} for part in path.parts):
|
||||
continue
|
||||
if path.suffix not in {".js", ".html", ".css"}:
|
||||
if path.suffix not in {".js", ".ts", ".tsx", ".jsx", ".html", ".css"}:
|
||||
continue
|
||||
files.append(str(path.relative_to(ROOT)))
|
||||
text_parts.append(read_text(path))
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check ordinary-user delivery paths before publishing a Jyotish build."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP = ROOT / "jyotish-app"
|
||||
|
||||
|
||||
def read(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def require(condition: bool, label: str, failures: list[str]) -> None:
|
||||
if not condition:
|
||||
failures.append(label)
|
||||
|
||||
|
||||
def delivery_matrix() -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"id": "local-dev",
|
||||
"label": "Local dev",
|
||||
"user_url": "http://127.0.0.1:5173",
|
||||
"commands": [
|
||||
".venv/bin/python scripts/jyotish_api_server.py --host 127.0.0.1 --port 5200",
|
||||
"cd jyotish-app && npm run dev -- --host 127.0.0.1 --port 5173",
|
||||
],
|
||||
"api_required": True,
|
||||
"scope": "Full web/app user experience with local API.",
|
||||
},
|
||||
{
|
||||
"id": "docker-compose",
|
||||
"label": "Docker Compose",
|
||||
"user_url": "http://localhost:5300",
|
||||
"commands": ["docker compose up -d"],
|
||||
"api_required": True,
|
||||
"scope": "Bundled API + web shell for local ordinary-user trials.",
|
||||
},
|
||||
{
|
||||
"id": "static-demo-pwa",
|
||||
"label": "Static demo / PWA",
|
||||
"user_url": "https://<static-host>/",
|
||||
"commands": ["cd jyotish-app && npm run build"],
|
||||
"api_required": False,
|
||||
"scope": "public demo shell; full advanced techniques require a local API service.",
|
||||
},
|
||||
{
|
||||
"id": "desktop-shell",
|
||||
"label": "Desktop shell",
|
||||
"user_url": "pwa://installed-app or pake://local-url",
|
||||
"commands": [
|
||||
"cd jyotish-app && npm run build && npm run preview -- --host 127.0.0.1 --port 4173",
|
||||
"python3 scripts/desktop_packaging_preflight.py",
|
||||
],
|
||||
"api_required": True,
|
||||
"scope": "PWA/Pake now; Tauri sidecar only after API lifecycle and signing are fixed.",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
failures: list[str] = []
|
||||
readme = read(ROOT / "README.md")
|
||||
dockerfile = read(ROOT / "Dockerfile")
|
||||
compose = read(ROOT / "docker-compose.yml")
|
||||
package = json.loads(read(APP / "package.json"))
|
||||
manifest = json.loads(read(APP / "public" / "manifest.webmanifest"))
|
||||
sw = read(APP / "public" / "sw.js")
|
||||
index_html = read(APP / "index.html")
|
||||
main_js = read(APP / "main.js")
|
||||
|
||||
require("普通用户交付形态" in readme, "README missing ordinary-user delivery matrix", failures)
|
||||
require("python3 scripts/deployment_preflight.py" in readme, "README missing deployment preflight command", failures)
|
||||
require("static_demo_boundary_visible" in readme, "README missing static demo boundary marker", failures)
|
||||
require('id="static-demo-boundary"' in index_html, "static demo capability boundary must be visible on first screen", failures)
|
||||
require("renderStaticDemoBoundary" in main_js, "Trust Center must render static demo capability boundary", failures)
|
||||
require("http://localhost:5300" in compose, "docker-compose missing ordinary web URL note", failures)
|
||||
require("python3 scripts/deployment_preflight.py" in dockerfile, "Dockerfile must run deployment preflight", failures)
|
||||
require("build" in package.get("scripts", {}), "jyotish-app missing build script", failures)
|
||||
require("preview" in package.get("scripts", {}), "jyotish-app missing preview script", failures)
|
||||
require(manifest.get("display") == "standalone", "PWA manifest must remain standalone", failures)
|
||||
require("url.pathname.startsWith('/api/')" in sw, "service worker must bypass API requests", failures)
|
||||
|
||||
result = {
|
||||
"valid": not failures,
|
||||
"failures": failures,
|
||||
"delivery_matrix": delivery_matrix(),
|
||||
"static_demo_boundary_visible": "static shell is labeled; local API-only capabilities are listed for ordinary users.",
|
||||
"ordinary_user_note": "公开演示环境只能完整展示静态壳;完整高级技法需要本地 API 服务。",
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0 if not failures else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,147 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check whether the Jyotish web app is ready for PWA/Pake/Tauri packaging."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP = ROOT / "jyotish-app"
|
||||
|
||||
|
||||
def read(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def require(condition: bool, label: str, failures: list[str]) -> None:
|
||||
if not condition:
|
||||
failures.append(label)
|
||||
|
||||
|
||||
def probe_command(binary: str, args: list[str] | None = None) -> dict:
|
||||
path = shutil.which(binary)
|
||||
result = {
|
||||
"binary": binary,
|
||||
"available": bool(path),
|
||||
"path": path,
|
||||
"version": None,
|
||||
}
|
||||
if not path:
|
||||
return result
|
||||
try:
|
||||
completed = subprocess.run(
|
||||
[path, *(args or ["--version"])],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=4,
|
||||
check=False,
|
||||
)
|
||||
result["version"] = (completed.stdout or completed.stderr).strip().splitlines()[0] if (completed.stdout or completed.stderr).strip() else ""
|
||||
except Exception as exc:
|
||||
result["version"] = f"probe_failed: {exc}"
|
||||
return result
|
||||
|
||||
|
||||
def toolchain_probe() -> dict:
|
||||
probes = {
|
||||
"node": probe_command("node"),
|
||||
"npm": probe_command("npm"),
|
||||
"rustc": probe_command("rustc"),
|
||||
"cargo": probe_command("cargo"),
|
||||
"xcodebuild": probe_command("xcodebuild", ["-version"]),
|
||||
"pake": probe_command("pake"),
|
||||
"tauri": probe_command("tauri"),
|
||||
}
|
||||
xcode_version = probes["xcodebuild"].get("version") or ""
|
||||
xcode_ready = bool(probes["xcodebuild"]["available"] and "requires Xcode" not in xcode_version)
|
||||
return {
|
||||
"non_destructive": True,
|
||||
"note": "Probe only checks local CLI presence/version; it does not run npm build, pake, tauri build, signing, notarization, or generate packages.",
|
||||
"license_gate": {
|
||||
"pake": "Pake upstream is GPL-3.0; review distribution compatibility before shipping a bundled desktop app.",
|
||||
"tauri": "Tauri app distribution still needs platform permissions, sidecar lifecycle design, signing_notarization, and Apple Developer decisions on macOS.",
|
||||
},
|
||||
"commands": probes,
|
||||
"readiness": {
|
||||
"pwa": True,
|
||||
"pake": bool(probes["node"]["available"] and probes["npm"]["available"] and probes["rustc"]["available"] and probes["cargo"]["available"] and probes["pake"]["available"]),
|
||||
"tauri": bool(probes["node"]["available"] and probes["npm"]["available"] and probes["rustc"]["available"] and probes["cargo"]["available"] and probes["tauri"]["available"]),
|
||||
"macos_signing_notarization": xcode_ready,
|
||||
},
|
||||
"warnings": [
|
||||
"xcodebuild exists but full Xcode is not selected; macOS signing_notarization is not ready."
|
||||
] if probes["xcodebuild"]["available"] and not xcode_ready else [],
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
failures: list[str] = []
|
||||
package = json.loads(read(APP / "package.json"))
|
||||
manifest = json.loads(read(APP / "public" / "manifest.webmanifest"))
|
||||
sw = read(APP / "public" / "sw.js")
|
||||
html = read(APP / "index.html")
|
||||
api_server = read(ROOT / "scripts" / "jyotish_api_server.py")
|
||||
main_js = read(APP / "main.js")
|
||||
click_smoke = read(ROOT / "tests" / "run_frontend_click_smoke.py")
|
||||
|
||||
scripts = package.get("scripts", {})
|
||||
require("build" in scripts, "jyotish-app/package.json missing build script", failures)
|
||||
require("preview" in scripts, "jyotish-app/package.json missing preview script", failures)
|
||||
require(manifest.get("name") == "Jyotish Vedic Astrology", "manifest name mismatch", failures)
|
||||
require(manifest.get("display") == "standalone", "manifest display must be standalone", failures)
|
||||
require(manifest.get("scope") == "/", "manifest scope must be /", failures)
|
||||
require(manifest.get("start_url") == "/", "manifest start_url must be /", failures)
|
||||
require(bool(manifest.get("theme_color")), "manifest theme_color missing", failures)
|
||||
require(any(icon.get("src") == "/pwa-icon.svg" for icon in manifest.get("icons", [])), "manifest icon missing", failures)
|
||||
require("CACHE_NAME = 'jyotish-shell-v1'" in sw, "service worker cache name missing", failures)
|
||||
require("url.pathname.startsWith('/api/')" in sw, "service worker must bypass API requests", failures)
|
||||
require("caches.match('/index.html')" in sw, "service worker fallback missing", failures)
|
||||
require('rel="manifest"' in html and "/manifest.webmanifest" in html, "index missing manifest link", failures)
|
||||
require("/pwa-icon.svg" in html, "index missing app icon", failures)
|
||||
require("JYOTISH_API_HOST', '127.0.0.1'" in api_server, "API host default must stay loopback", failures)
|
||||
require("Trust Center" in main_js and "Local-first" in main_js, "Trust Center status missing", failures)
|
||||
require("pwa-install" in main_js and "promptPWAInstall" in main_js, "PWA install action missing", failures)
|
||||
require(bool(re.search(r"127\.0\.0\.1:5200", main_js)), "Trust Center must show loopback API boundary", failures)
|
||||
require("tests/run_frontend_click_smoke.py" in read(ROOT / "scripts" / "run_quality_gate.py"), "quality gate must run browser click smoke", failures)
|
||||
require("--mode" in click_smoke and "all" in click_smoke, "click smoke must support --mode all", failures)
|
||||
require("offline_recovery_guidance_visible" in click_smoke, "click smoke must verify offline recovery guidance", failures)
|
||||
require("manifest.webmanifest" in click_smoke and "serviceWorker" in click_smoke, "click smoke must verify PWA installed shell", failures)
|
||||
|
||||
if failures:
|
||||
print(json.dumps({"valid": False, "failures": failures}, ensure_ascii=False, indent=2))
|
||||
return 1
|
||||
first_launch_checks = [
|
||||
{
|
||||
"path": "PWA installed shell",
|
||||
"command": "python3 tests/run_frontend_click_smoke.py --mode all",
|
||||
"expected": "manifest.webmanifest, serviceWorker, mobile shell, online workflow, and offline recovery guidance",
|
||||
},
|
||||
{
|
||||
"path": "Pake first launch",
|
||||
"command": "cd jyotish-app && npm run build && npm run preview -- --host 127.0.0.1 --port 4173",
|
||||
"expected": "URL shell can open the built app; local API still needs python3 scripts/jyotish_api_server.py on 127.0.0.1:5200",
|
||||
},
|
||||
{
|
||||
"path": "Tauri sidecar readiness",
|
||||
"command": "python3 scripts/desktop_packaging_preflight.py",
|
||||
"expected": "loopback API boundary, sidecar route, manifest, service worker, and Trust Center remain visible before scaffolding",
|
||||
},
|
||||
]
|
||||
print(json.dumps({
|
||||
"valid": True,
|
||||
"packaging_paths": ["pwa", "pake-url-shell", "tauri-sidecar-spike"],
|
||||
"app_dir": str(APP),
|
||||
"api_default": "127.0.0.1:5200",
|
||||
"first_launch_checks": first_launch_checks,
|
||||
"toolchain_probe": toolchain_probe(),
|
||||
}, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -37,12 +37,7 @@ def _python_module_status(module_name: str) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _package_has_dependency(package_text: str, dependency: str) -> bool:
|
||||
return f'"{dependency}"' in package_text or f"'{dependency}'" in package_text
|
||||
|
||||
|
||||
def build_probe() -> dict[str, Any]:
|
||||
package_json = _read_text("jyotish-app", "package.json")
|
||||
product_gap = _read_text("docs", "research", "product_gap_matrix_2026_06_22.md")
|
||||
open_source_scan = _read_text("docs", "research", "open_source_scan_2026_06_22.md")
|
||||
|
||||
@@ -50,12 +45,6 @@ def build_probe() -> dict[str, Any]:
|
||||
swisseph_python_available = swisseph_python_module["available"] and "import swisseph as swe" in _read_text(
|
||||
"scripts", "jyotish_api_server.py"
|
||||
)
|
||||
swisseph_wasm_files = [
|
||||
"jyotish-app/lib/swisseph-wasm/swisseph.js",
|
||||
"jyotish-app/public/swisseph/swisseph.js",
|
||||
]
|
||||
swisseph_wasm_available = any(_exists(*path.split("/")) for path in swisseph_wasm_files)
|
||||
|
||||
vedastro_local = _exists("references", "open_source_sources", "VedicAstro")
|
||||
vedastro_scan = "VedAstro/VedAstro" in open_source_scan or "VedAstro/VedAstro" in product_gap
|
||||
external_benchmark_scan = "naturalstupid/PyJHora" in open_source_scan or "PyJHora" in product_gap
|
||||
@@ -72,21 +61,6 @@ def build_probe() -> dict[str, Any]:
|
||||
],
|
||||
"next_step": "keep as canonical longitude source until another backend passes parity cases",
|
||||
},
|
||||
"swisseph_wasm": {
|
||||
"available": bool(swisseph_wasm_available),
|
||||
"replacement_readiness": "fallback",
|
||||
"license_posture": "browser fallback using Swiss Ephemeris WASM assets; same boundary as Swiss Ephemeris",
|
||||
"evidence": [
|
||||
path for path in swisseph_wasm_files if _exists(*path.split("/"))
|
||||
]
|
||||
+ [
|
||||
{
|
||||
"@swisseph/browser": _package_has_dependency(package_json, "@swisseph/browser"),
|
||||
"swisseph-wasm": _package_has_dependency(package_json, "swisseph-wasm"),
|
||||
}
|
||||
],
|
||||
"next_step": "keep for local-first browser degradation, not as a separate accuracy baseline",
|
||||
},
|
||||
"xalen_ephemeris": {
|
||||
"available": False,
|
||||
"replacement_readiness": "spike_only",
|
||||
@@ -130,7 +104,6 @@ def build_probe() -> dict[str, Any]:
|
||||
},
|
||||
"replacement_readiness": {
|
||||
"primary": ["swisseph_python"],
|
||||
"fallback": ["swisseph_wasm"],
|
||||
"spike_only": ["xalen_ephemeris"],
|
||||
"service_adapter_candidate": ["vedastro"],
|
||||
"benchmark_only": ["external_benchmark_benchmark"],
|
||||
|
||||
@@ -20,54 +20,7 @@ def _exists(*parts: str) -> bool:
|
||||
return ROOT.joinpath(*parts).exists()
|
||||
|
||||
|
||||
def _read(*parts: str) -> str:
|
||||
path = ROOT.joinpath(*parts)
|
||||
if not path.exists():
|
||||
return ""
|
||||
return path.read_text(encoding="utf-8", errors="ignore")
|
||||
|
||||
|
||||
def _package_dependency(name: str) -> bool:
|
||||
package = _read("jyotish-app", "package.json")
|
||||
return f'"{name}"' in package or f"'{name}'" in package
|
||||
|
||||
|
||||
def _package_license(*parts: str) -> str:
|
||||
text = _read(*parts)
|
||||
if not text:
|
||||
return "not_found"
|
||||
try:
|
||||
data = json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
return "unreadable"
|
||||
return str(data.get("license") or "unspecified")
|
||||
|
||||
|
||||
def build_spike() -> Dict[str, Any]:
|
||||
swisseph_wasm_assets = [
|
||||
"jyotish-app/public/swisseph-wasm/wasm/swisseph.wasm",
|
||||
"jyotish-app/public/swisseph/swisseph.wasm",
|
||||
"jyotish-app/lib/swisseph-wasm/swisseph.js",
|
||||
]
|
||||
swisseph_wasm_candidate = {
|
||||
"candidate_backend": "swisseph_wasm_candidate",
|
||||
"candidate_adapter_spike": "asset_detected_no_runtime_switch",
|
||||
"available": any(_exists(*path.split("/")) for path in swisseph_wasm_assets),
|
||||
"evidence": [path for path in swisseph_wasm_assets if _exists(*path.split("/"))],
|
||||
"dependencies": {
|
||||
"@swisseph/browser": _package_dependency("@swisseph/browser"),
|
||||
"swisseph-wasm": _package_dependency("swisseph-wasm"),
|
||||
},
|
||||
"package_license": {
|
||||
"@swisseph/browser": _package_license("jyotish-app", "node_modules", "@swisseph", "browser", "package.json"),
|
||||
"swisseph-wasm": _package_license("jyotish-app", "node_modules", "swisseph-wasm", "package.json"),
|
||||
},
|
||||
"license_gate": "Swiss Ephemeris WASM remains under Swiss Ephemeris licensing boundaries; verify commercial/GPL compatibility before distribution claims.",
|
||||
"distribution_gate": "AGPL-3.0 and GPL-3.0-or-later packages must not be treated as low-risk proprietary desktop/PWA dependencies.",
|
||||
"runtime_setting_exposure": "blocked_until_parity_gate_required",
|
||||
"parity_gate_required": "Must emit EphemerisAdapterContract rows and pass swisseph_python longitude_delta_arcsec thresholds.",
|
||||
}
|
||||
|
||||
xalen_local_paths = [
|
||||
"references/open_source_sources/xalen-ephemeris",
|
||||
"references/open_source_sources/xalen",
|
||||
@@ -102,14 +55,11 @@ def build_spike() -> Dict[str, Any]:
|
||||
"candidate_adapter_spike": True,
|
||||
"runtime_setting_exposure": "do_not_expose_non_swisseph_backend_yet",
|
||||
"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,
|
||||
},
|
||||
|
||||
@@ -18,7 +18,6 @@ from scripts.strict_evidence_service import existing_interpretation_source_pack
|
||||
|
||||
REQUIRED_LAYERS = [
|
||||
"primary_truth",
|
||||
"frontend_interpretation",
|
||||
"qa_governance",
|
||||
"reader_validation",
|
||||
"yoga_rules",
|
||||
@@ -29,7 +28,7 @@ REQUIRED_LAYERS = [
|
||||
SCAN_ROOTS = [
|
||||
"references",
|
||||
"docs",
|
||||
"jyotish-app",
|
||||
"frontend",
|
||||
"assets",
|
||||
"SKILL.md",
|
||||
"AGENTS.md",
|
||||
@@ -271,7 +270,7 @@ def _classify_candidate(path: str, runtime_source_refs: set[str], layer_refs: se
|
||||
"promotion_status": "benchmark_evidence_only",
|
||||
"reason": "Benchmark or evidence report; use for validation boundaries, not direct rules.",
|
||||
}
|
||||
if path.startswith("jyotish-app/"):
|
||||
if path.startswith("frontend/"):
|
||||
return {
|
||||
"classification": "frontend_surface",
|
||||
"priority": "priority_3",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
印度占星 API 服务器 v1.0
|
||||
为 jyotish-app 前端提供 v6.9.14 引擎的精算能力
|
||||
为 Next.js 产品前端提供 v6.9.14 引擎的精算能力
|
||||
|
||||
启动: python3 scripts/jyotish_api_server.py --port 5200
|
||||
"""
|
||||
@@ -1304,8 +1304,8 @@ DEFAULT_ALLOWED_ORIGINS = {
|
||||
'http://127.0.0.1:3456',
|
||||
'http://localhost:3457',
|
||||
'http://127.0.0.1:3457',
|
||||
'http://localhost:5173',
|
||||
'http://127.0.0.1:5173',
|
||||
'http://localhost:3000',
|
||||
'http://127.0.0.1:3000',
|
||||
}
|
||||
DEFAULT_ALLOWED_HOSTS = {'localhost', '127.0.0.1', '::1'}
|
||||
MAX_REQUEST_BYTES = 2 * 1024 * 1024
|
||||
@@ -7582,7 +7582,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
techniques = registry.get('techniques', {})
|
||||
engine_commands = self._scan_engine_commands()
|
||||
api_endpoints = self._scan_api_endpoints()
|
||||
app_tabs = self._scan_app_tabs()
|
||||
app_routes = self._scan_app_routes()
|
||||
local_sources = self._scan_local_open_source_sources()
|
||||
command_set = set(engine_commands)
|
||||
api_command_map = API_COMMAND_MAP
|
||||
@@ -7591,7 +7591,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
for command, endpoint in api_command_map.items()
|
||||
if endpoint in api_endpoints or command in command_set
|
||||
)
|
||||
app_visible_topics = self._app_visible_topics(app_tabs)
|
||||
app_visible_topics = self._app_visible_topics()
|
||||
registry_commands = sorted({
|
||||
command
|
||||
for technique in techniques.values()
|
||||
@@ -7652,8 +7652,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'api_backed_commands': api_backed_commands,
|
||||
'engine_not_api': engine_not_api,
|
||||
'registry_only_commands': registry_only_commands,
|
||||
'app_tab_count': len(app_tabs),
|
||||
'app_tabs': app_tabs,
|
||||
'app_route_count': len(app_routes),
|
||||
'app_routes': app_routes,
|
||||
'app_visible_topics': app_visible_topics,
|
||||
},
|
||||
'local_open_source': {
|
||||
@@ -8111,33 +8111,40 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
return []
|
||||
return sorted(set(re.findall(r"path == ['\"](/api/[^'\"]+)['\"]", text)))
|
||||
|
||||
def _scan_app_tabs(self):
|
||||
path = os.path.join(REPO_ROOT, 'jyotish-app', 'index.html')
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
except OSError:
|
||||
def _scan_app_routes(self):
|
||||
root = os.path.join(REPO_ROOT, 'frontend', 'src', 'app')
|
||||
if not os.path.isdir(root):
|
||||
return []
|
||||
return sorted(set(re.findall(r'data-tab="([^"]+)"', text)))
|
||||
routes = []
|
||||
for dirpath, _, filenames in os.walk(root):
|
||||
if not {'page.tsx', 'page.ts', 'page.jsx', 'page.js'}.intersection(filenames):
|
||||
continue
|
||||
relative = os.path.relpath(dirpath, root)
|
||||
routes.append('home' if relative == '.' else relative.replace(os.sep, '/'))
|
||||
return sorted(set(routes))
|
||||
|
||||
def _scan_app_source_text(self):
|
||||
root = os.path.join(REPO_ROOT, 'jyotish-app')
|
||||
if not os.path.isdir(root):
|
||||
return ''
|
||||
roots = [
|
||||
os.path.join(REPO_ROOT, 'frontend', 'src', 'app'),
|
||||
os.path.join(REPO_ROOT, 'frontend', 'src', 'components'),
|
||||
]
|
||||
chunks = []
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
dirnames[:] = [
|
||||
d for d in dirnames
|
||||
if d not in {'node_modules', 'dist', '.vite', '.git'}
|
||||
]
|
||||
for filename in filenames:
|
||||
if not filename.endswith(('.js', '.html', '.css')):
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(dirpath, filename), 'r', encoding='utf-8', errors='ignore') as f:
|
||||
chunks.append(f.read(40000))
|
||||
except OSError:
|
||||
continue
|
||||
for root in roots:
|
||||
if not os.path.isdir(root):
|
||||
continue
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
dirnames[:] = [
|
||||
d for d in dirnames
|
||||
if d not in {'api', 'node_modules', '.next', '.git'}
|
||||
]
|
||||
for filename in filenames:
|
||||
if not filename.endswith(('.js', '.jsx', '.ts', '.tsx', '.html', '.css')):
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(dirpath, filename), 'r', encoding='utf-8', errors='ignore') as f:
|
||||
chunks.append(f.read(40000))
|
||||
except OSError:
|
||||
continue
|
||||
return '\n'.join(chunks).lower()
|
||||
|
||||
def _scan_local_open_source_sources(self):
|
||||
@@ -8258,29 +8265,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
found.append(module)
|
||||
return found
|
||||
|
||||
def _app_visible_topics(self, tabs):
|
||||
mapping = {
|
||||
'chart': 'D1/Rashi',
|
||||
'complete': 'Full Reading',
|
||||
'karaka': 'Jaimini Karaka',
|
||||
'houses': 'Bhava',
|
||||
'aspects': 'Aspects',
|
||||
'yogas': 'Yoga',
|
||||
'vargas': 'Varga',
|
||||
'ashtakavarga': 'Ashtakavarga',
|
||||
'shadbala': 'Shadbala',
|
||||
'dasha': 'Dasha',
|
||||
'transit': 'Transit',
|
||||
'deep': 'PACDARES/Argala',
|
||||
'extended': 'Bhava Bala/Vimsopaka',
|
||||
'remedies': 'Remedies',
|
||||
'synastry': 'Synastry',
|
||||
'prashna': 'Prashna',
|
||||
'kp': 'KP',
|
||||
'verify': 'Verification',
|
||||
'transit-compare': 'Transit Compare',
|
||||
}
|
||||
topics = [mapping[t] for t in tabs if t in mapping]
|
||||
def _app_visible_topics(self):
|
||||
topics = []
|
||||
source_text = self._scan_app_source_text()
|
||||
source_markers = {
|
||||
'Muhurta': ['computemuhurta', 'muhurta'],
|
||||
|
||||
@@ -1835,9 +1835,6 @@ def _build_ai_prompt_pack(report):
|
||||
'references/raman-house-judgment-methodology.md',
|
||||
'references/mandatory-verification-gate-protocol.md',
|
||||
'references/real-reading-quality-checklist.md',
|
||||
'jyotish-app/planet-house-details-a.js',
|
||||
'jyotish-app/planet-house-details-b.js',
|
||||
'jyotish-app/planet-house-details-c.js',
|
||||
],
|
||||
'retrieval_tags': [
|
||||
'no_single_factor_conclusion',
|
||||
|
||||
@@ -18,7 +18,7 @@ sys.path.insert(0, str(ROOT / "scripts"))
|
||||
from local_env import load_local_env # noqa: E402
|
||||
|
||||
load_local_env(ROOT)
|
||||
APP = ROOT / "jyotish-app"
|
||||
APP = ROOT / "frontend"
|
||||
PYTHON = sys.executable
|
||||
|
||||
COMPILE_DIRS = [
|
||||
@@ -37,14 +37,11 @@ EXTRA_COMPILE_TARGETS = [
|
||||
ROOT / "scripts" / "oracle_collection_queue.py",
|
||||
ROOT / "scripts" / "oracle_evidence_validator.py",
|
||||
ROOT / "scripts" / "sync_final_evidence_packet_status.py",
|
||||
ROOT / "scripts" / "deployment_preflight.py",
|
||||
ROOT / "tests" / "run_golden_cases.py",
|
||||
ROOT / "tests" / "run_real_case_revalidation.py",
|
||||
ROOT / "tests" / "run_frontend_runtime_smoke.py",
|
||||
]
|
||||
|
||||
CORE_PYTEST_TARGETS = [
|
||||
"tests/test_frontend_productization.py",
|
||||
"tests/test_cli_smoke.py",
|
||||
"tests/test_api_server_security.py",
|
||||
"tests/test_jaimini.py",
|
||||
@@ -63,7 +60,6 @@ RUNTIME_TRUTH_PYTEST_TARGETS = [
|
||||
"tests/test_interpretation_source_inventory_gate.py::test_quality_gate_runs_interpretation_source_inventory_gate",
|
||||
"tests/test_interpretation_source_runtime_coverage.py",
|
||||
"tests/test_final_jhora_evidence_packet_acceptance.py",
|
||||
"tests/test_frontend_productization.py::test_result_page_surfaces_workflow_summary_and_provenance_detail",
|
||||
]
|
||||
|
||||
RELEASE_CRITICAL_UNTRACKED_PATHS = [
|
||||
@@ -75,19 +71,10 @@ RELEASE_CRITICAL_UNTRACKED_PATHS = [
|
||||
"docs/research/product_gap_matrix_2026_06_22.md",
|
||||
"docs/research/whole_machine_git_audit_2026_06_23.md",
|
||||
"findings.md",
|
||||
"jyotish-app/import-chart.js",
|
||||
"jyotish-app/mevg-audit.js",
|
||||
"jyotish-app/public/manifest.webmanifest",
|
||||
"jyotish-app/public/pwa-icon.svg",
|
||||
"jyotish-app/public/sw.js",
|
||||
"jyotish-app/security.js",
|
||||
"jyotish-app/skill-map.js",
|
||||
"progress.md",
|
||||
"references/oracle/dasha_shadbala_oracle_cases.json",
|
||||
"scripts/audit_fragments.py",
|
||||
"scripts/deep_varga_avastha.py",
|
||||
"scripts/deployment_preflight.py",
|
||||
"scripts/desktop_packaging_preflight.py",
|
||||
"scripts/dasha_reference_audit.py",
|
||||
"scripts/ephemeris_adapter_contract.py",
|
||||
"scripts/ephemeris_backend_probe.py",
|
||||
@@ -96,12 +83,9 @@ RELEASE_CRITICAL_UNTRACKED_PATHS = [
|
||||
"scripts/oracle_collection_queue.py",
|
||||
"scripts/oracle_evidence_validator.py",
|
||||
"task_plan.md",
|
||||
"tests/run_frontend_click_smoke.py",
|
||||
"tests/run_frontend_runtime_smoke.py",
|
||||
"tests/test_api_server_security.py",
|
||||
"tests/test_dasha_reference_audit.py",
|
||||
"tests/test_deep_varga_avastha.py",
|
||||
"tests/test_frontend_productization.py",
|
||||
"tests/test_oracle_boundary_audit.py",
|
||||
"tests/test_oracle_collection_queue.py",
|
||||
"tests/test_oracle_evidence_validator.py",
|
||||
@@ -112,8 +96,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": True,
|
||||
"skip_yoga_logic": True,
|
||||
"skip_frontend_runtime": False,
|
||||
"skip_frontend_click": True,
|
||||
"frontend_click_mode": "core",
|
||||
"check_release_hygiene": False,
|
||||
"skip_real_cases": True,
|
||||
"skip_dasha_audit": True,
|
||||
@@ -125,8 +107,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": True,
|
||||
"skip_yoga_logic": True,
|
||||
"skip_frontend_runtime": False,
|
||||
"skip_frontend_click": False,
|
||||
"frontend_click_mode": "all",
|
||||
"check_release_hygiene": False,
|
||||
"skip_real_cases": True,
|
||||
"skip_dasha_audit": True,
|
||||
@@ -138,8 +118,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": False,
|
||||
"skip_yoga_logic": False,
|
||||
"skip_frontend_runtime": False,
|
||||
"skip_frontend_click": False,
|
||||
"frontend_click_mode": "all",
|
||||
"check_release_hygiene": True,
|
||||
"skip_real_cases": False,
|
||||
"skip_dasha_audit": False,
|
||||
@@ -151,8 +129,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": True,
|
||||
"skip_yoga_logic": False,
|
||||
"skip_frontend_runtime": True,
|
||||
"skip_frontend_click": True,
|
||||
"frontend_click_mode": "core",
|
||||
"check_release_hygiene": False,
|
||||
"skip_real_cases": False,
|
||||
"skip_dasha_audit": False,
|
||||
@@ -164,8 +140,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": True,
|
||||
"skip_yoga_logic": True,
|
||||
"skip_frontend_runtime": True,
|
||||
"skip_frontend_click": True,
|
||||
"frontend_click_mode": "core",
|
||||
"check_release_hygiene": False,
|
||||
"skip_real_cases": True,
|
||||
"skip_dasha_audit": True,
|
||||
@@ -177,8 +151,6 @@ QUALITY_GATE_PROFILES = {
|
||||
"skip_slow": True,
|
||||
"skip_yoga_logic": True,
|
||||
"skip_frontend_runtime": True,
|
||||
"skip_frontend_click": True,
|
||||
"frontend_click_mode": "core",
|
||||
"check_release_hygiene": False,
|
||||
"skip_real_cases": True,
|
||||
"skip_dasha_audit": True,
|
||||
@@ -301,10 +273,9 @@ def format_failure_summary(
|
||||
lines.extend([
|
||||
"普通用户启动路径:",
|
||||
"1. 本地 API 服务:.venv/bin/python scripts/jyotish_api_server.py --host 127.0.0.1 --port 5200",
|
||||
"2. 网页服务:cd jyotish-app && npm run dev -- --host 127.0.0.1 --port 5173",
|
||||
"3. Open http://127.0.0.1:5173, then open Trust Center and run the health check.",
|
||||
"4. PWA 安装壳只包装网页服务,本地 API 服务仍需单独启动。",
|
||||
"Next action: Run the focused command above, add --keep-logs for browser click smoke, then compare the app state with the startup path above.",
|
||||
"2. 网页服务:npm run dev --prefix frontend",
|
||||
"3. Open http://127.0.0.1:3000 and verify /api/health.",
|
||||
"Next action: Run the focused command above, then rerun the affected Python or Next.js check.",
|
||||
"",
|
||||
])
|
||||
return "\n".join(lines)
|
||||
@@ -469,7 +440,6 @@ def run_profile(args: argparse.Namespace) -> dict:
|
||||
"skip_slow",
|
||||
"skip_yoga_logic",
|
||||
"skip_frontend_runtime",
|
||||
"skip_frontend_click",
|
||||
"skip_real_cases",
|
||||
"skip_dasha_audit",
|
||||
"skip_oracle_audit",
|
||||
@@ -478,8 +448,6 @@ def run_profile(args: argparse.Namespace) -> dict:
|
||||
]:
|
||||
if getattr(args, key):
|
||||
profile[key] = True
|
||||
if args.frontend_click_mode:
|
||||
profile["frontend_click_mode"] = args.frontend_click_mode
|
||||
return profile
|
||||
|
||||
|
||||
@@ -488,15 +456,12 @@ def main() -> int:
|
||||
parser.add_argument("--profile", choices=["quick", "browser", "release", "accuracy", "vedastro-live", "runtime-truth"], default="browser", help="Quality gate profile: quick, browser, release, accuracy, vedastro-live, or runtime-truth")
|
||||
parser.add_argument("--skip-slow", action="store_true", help="Skip slow golden-case regressions")
|
||||
parser.add_argument("--skip-yoga-logic", action="store_true", help="Skip Yoga logic comparison report refresh")
|
||||
parser.add_argument("--skip-frontend-runtime", action="store_true", help="Skip frontend build and runtime smoke")
|
||||
parser.add_argument("--skip-frontend-click", action="store_true", help="Skip browser click smoke")
|
||||
parser.add_argument("--skip-frontend-runtime", action="store_true", help="Skip Next.js tests, lint, and production build")
|
||||
parser.add_argument("--skip-real-cases", action="store_true", help="Skip public real-person chart revalidation")
|
||||
parser.add_argument("--skip-dasha-audit", action="store_true", help="Skip Dasha reference-drift audit")
|
||||
parser.add_argument("--skip-oracle-audit", action="store_true", help="Skip combined Dasha/Shadbala external oracle boundary audit")
|
||||
parser.add_argument("--skip-local-accuracy-report", action="store_true", help="Skip consolidated local accuracy report")
|
||||
parser.add_argument("--skip-vedastro-live", action="store_true", help="Skip optional VedAstro live endpoint smoke")
|
||||
parser.add_argument("--frontend-click-mode", choices=["core", "mobile", "offline", "pdf", "workspace", "mobile-trust", "import-files", "all"], default=None, help="Browser click smoke mode for browser/release profiles")
|
||||
parser.add_argument("--frontend-click-timeout", type=int, default=240, help="Timeout seconds for browser click smoke")
|
||||
parser.add_argument("--all-tests", action="store_true", help="Run every pytest file, including optional-dependency suites")
|
||||
parser.add_argument("--require-external-parity", action="store_true", help="Fail the release gate unless the three-engine raw parity manifest passes.")
|
||||
args = parser.parse_args()
|
||||
@@ -528,7 +493,6 @@ def main() -> int:
|
||||
run([PYTHON, "scripts/audit_fragments.py", "--strict"])
|
||||
run([PYTHON, "scripts/interpretation_source_inventory_gate.py"])
|
||||
run([PYTHON, "scripts/character_level_inventory_manifest.py", "--scope", "project", "--no-write", "--summary-only"])
|
||||
run([PYTHON, "scripts/deployment_preflight.py"])
|
||||
if profile["check_release_hygiene"]:
|
||||
release_hygiene_check(require_external_parity=args.require_external_parity)
|
||||
run([PYTHON, "scripts/validate_bphs_invariants.py"])
|
||||
@@ -540,17 +504,9 @@ def main() -> int:
|
||||
pytest_targets = CORE_PYTEST_TARGETS
|
||||
run([PYTHON, "-m", "pytest", *pytest_targets])
|
||||
if not profile["skip_frontend_runtime"]:
|
||||
run(["npm", "test"], optional=False, cwd=APP)
|
||||
run(["npm", "run", "lint"], optional=False, cwd=APP)
|
||||
run(["npm", "run", "build"], optional=False, cwd=APP)
|
||||
run([PYTHON, "tests/run_frontend_runtime_smoke.py", "--start-if-needed"])
|
||||
if not profile["skip_frontend_click"]:
|
||||
run([
|
||||
PYTHON,
|
||||
"tests/run_frontend_click_smoke.py",
|
||||
"--mode",
|
||||
profile["frontend_click_mode"],
|
||||
"--timeout",
|
||||
str(args.frontend_click_timeout),
|
||||
])
|
||||
if not profile["skip_slow"]:
|
||||
run([PYTHON, "tests/run_golden_cases.py", "--python", PYTHON])
|
||||
if not profile["skip_real_cases"]:
|
||||
|
||||
@@ -17,7 +17,6 @@ def build_audit(root: Path) -> dict:
|
||||
full_reading = root / "scripts/full_reading.py"
|
||||
api_server = root / "scripts/jyotish_api_server.py"
|
||||
deep = root / "scripts/deep_varga_avastha.py"
|
||||
skill_map = root / "jyotish-app/skill-map.js"
|
||||
tests = root / "tests/test_cli_smoke.py"
|
||||
deep_tests = root / "tests/test_deep_varga_avastha.py"
|
||||
|
||||
@@ -43,11 +42,10 @@ def build_audit(root: Path) -> dict:
|
||||
"main_artifacts": [
|
||||
"scripts/deep_varga_avastha.py",
|
||||
"scripts/jyotish_api_server.py",
|
||||
"jyotish-app/skill-map.js",
|
||||
"tests/test_deep_varga_avastha.py",
|
||||
],
|
||||
"historical_artifacts": ["skills/jyotish-engine-modules/scripts/avastha_calculator.py"],
|
||||
"entrypoints": ["/api/deep_varga_avastha", "jyotish-app skill-map deepVargaAvastha"],
|
||||
"entrypoints": ["/api/deep_varga_avastha"],
|
||||
"reuse_decision": "do_not_duplicate_runtime",
|
||||
"next_action": "add_display_contract_and_source_oracle_packet",
|
||||
"claim_boundary": "Avastha is endpoint-visible, but formula variants and interpretive claim level still need source/oracle packet.",
|
||||
|
||||
@@ -170,7 +170,7 @@ VEDASTRO_CAPABILITY_SEEDS: list[dict[str, Any]] = [
|
||||
"vedastro_capability": "Report Rendering",
|
||||
"category": "presentation",
|
||||
"domains": ["report", "image"],
|
||||
"local_assets": ["report_artifact API", "report_builder.py", "chart_renderer.py", "jyotish-app export"],
|
||||
"local_assets": ["report_artifact API", "report_builder.py", "chart_renderer.py"],
|
||||
"can_call_vedastro": False,
|
||||
"recommended_path": "new_local_impl",
|
||||
"fastest_path_lane": "local_native_preferred",
|
||||
@@ -218,7 +218,7 @@ VEDASTRO_CAPABILITY_SEEDS: list[dict[str, Any]] = [
|
||||
"vedastro_capability": "Birth Time ML / Rectification Assistant",
|
||||
"category": "birth_time_rectification",
|
||||
"domains": ["birth"],
|
||||
"local_assets": ["birth_time_rectifier.py", "rectification_gate", "jyotish-app rectification"],
|
||||
"local_assets": ["birth_time_rectifier.py", "rectification_gate"],
|
||||
"can_call_vedastro": True,
|
||||
"recommended_path": "hybrid_local_plus_vedastro",
|
||||
"fastest_path_lane": "rest_adapter",
|
||||
|
||||
Reference in New Issue
Block a user