From c7350bcafbff64d3c526c2dbf3995c9845b3b66e Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Fri, 16 Oct 2026 10:24:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=8A=E6=98=9F=E7=9B=98=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=9A=84=E8=AE=A1=E7=AE=97=E5=92=8C=20HTTP=20=E6=8B=86?= =?UTF-8?q?=E6=88=90=E4=B8=A4=E4=B8=AA=E7=B1=BB=EF=BC=8C=E7=A6=BB=E7=BA=BF?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E4=B8=8D=E5=86=8D=E4=BC=AA=E9=80=A0=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=AF=B9=E8=B1=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 方法体未改。定向测试 383 通过;1 条校正回归在基线上同样失败,断言未改。快速门和全量 pytest 未跑。 Co-Authored-By: Grok (agent loop) --- ...ESS-api-server-service-extract-20261013.md | 88 + .../capture_report_blocked_repairs_golden.py | 6 +- scripts/consultation_workflow_service.py | 4 +- scripts/jyotish_api_server.py | 1174 +++++----- .../research/api_service_extract_golden.py | 273 +++ .../golden/api_server_service_extract_v1.json | 1966 +++++++++++++++++ tests/test_active_rectification_api.py | 12 +- tests/test_active_rectification_events_v4.py | 4 +- tests/test_api_server_growth_contract.py | 120 +- tests/test_api_service_extract_golden.py | 17 + tests/test_calculation_p0_regressions.py | 4 +- ..._commercial_domain_calculation_contract.py | 14 +- tests/test_consultation_birth_accuracy.py | 6 +- tests/test_consultation_consumer_context.py | 6 +- tests/test_consultation_plan_contract.py | 12 +- ...ltation_workflow_birth_time_sensitivity.py | 8 +- tests/test_consultation_workflow_domains.py | 8 +- tests/test_declared_window_chart.py | 4 +- tests/test_flexible_birth_time_engine.py | 4 +- tests/test_health_narrative.py | 4 +- tests/test_jaimini_rangacharya_api.py | 6 +- tests/test_prashna_entry_contract.py | 4 +- .../test_relationship_event_class_evidence.py | 4 +- tests/test_thematic_dasha_info_source.py | 6 +- tests/test_vedastro_snapshot_cache.py | 8 +- 25 files changed, 3104 insertions(+), 658 deletions(-) create mode 100644 docs/tasks/PROGRESS-api-server-service-extract-20261013.md create mode 100644 scripts/research/api_service_extract_golden.py create mode 100644 tests/golden/api_server_service_extract_v1.json create mode 100644 tests/test_api_service_extract_golden.py diff --git a/docs/tasks/PROGRESS-api-server-service-extract-20261013.md b/docs/tasks/PROGRESS-api-server-service-extract-20261013.md new file mode 100644 index 00000000..46f44378 --- /dev/null +++ b/docs/tasks/PROGRESS-api-server-service-extract-20261013.md @@ -0,0 +1,88 @@ +# PROGRESS · API 计算与 HTTP 拆类(2026-10-16) + +- 分支:`codex/api-server-service-extract-20261013` +- 任务书里的基线 `2994588ad4d858dead4835943e5883c5aa2b03f8` 已过期。本分支从当前 `origin/staging` `fbf04f0b5f31cd8421bc4546840b1c833aaed810` 新建,没有上游。该提交已包含 #69 与 #68。 +- 没有新 BUG 编号。结构改造,不写 `docs/BUG_HISTORY.md`,不写 `CHANGELOG.md`。 + +## 开工重测 + +| 指标 | 任务书写作时 | 开工实测 | +| --- | --- | --- | +| `scripts/jyotish_api_server.py` 换行数 | 10970 | **10906** | +| 四空格 `def`(合同口径) | 218 | **217**(BUG-1341 已下调,本单保持) | +| `JyotishAPIHandler` 类方法 | 215 | **214** | +| `JyotishAPIHandler.__new__` 合计 | 31 | **31**(`scripts/` 2 + `tests/` 29,不含合同文件) | +| 行数基线 / 上限 | 10924 / 11224 | 基线不动。搬后换行数 **10920**,低于基线 | + +HTTP 侧按「直接碰 HTTP 成员或覆写 `BaseHTTPRequestHandler`,再向上收到调用闭包」重算,仍是 15 个方法,外加 `server_version`: + +- 直接:`_json`、`_html`、`_send_cors_headers`、`_enforce_request_security`、`_job_access_token`、`do_OPTIONS`、`do_GET`、`do_POST`、`_read_json_body` +- 调用者:`_error_json`、`_require_dynamic_rectification_token`、`_get_high_rigor_job`、`_get_chart_job`、`_compute_dynamic_rectification_opportunities`、`_compute_dynamic_rectification_score` + +类里没有 `self.xxx =` 赋值。 + +## 做了什么 + +- 同一文件里把原类改成 `JyotishComputeService`(199 个计算方法),新增 `JyotishAPIHandler(BaseHTTPRequestHandler, JyotishComputeService)`,放入上面 15 个方法和 `server_version`。`ThreadingHTTPServer` 仍用 `JyotishAPIHandler`。 +- 方法体与基线逐个 `ast.dump` 相等:旧类 214 个方法,新文件 214 个方法,不一致 **0**。换类的成员是这 15 个方法加 `server_version`,共 16 个。 +- `scripts/consultation_workflow_service.py` 和 `scripts/capture_report_blocked_repairs_golden.py` 改为 `JyotishComputeService()`。`scripts/` 里 `JyotishAPIHandler.__new__` 为 **0**。 +- 只用计算方法的 17 个测试文件同样改了构造。`tests/test_active_rectification_api.py` 里 3 处补丁从 HTTP 类改到计算类,否则补丁打不中实例。断言没改。 +- 增长合同:总伪造 31 → **5**,生产伪造 2 → **0**,方法数 217 不变,行数基线 10924 不动。新增两条守卫:计算类不是 HTTP 处理器子类(并钉住 MRO 与 `server_version`);计算类方法体不碰 HTTP 成员、也不调用上述 15 个方法。 +- 搬前 golden 连跑两次哈希一致,落在 `tests/golden/api_server_service_extract_v1.json`(只存 sha256 和键路径类型)。采集脚本:`scripts/research/api_service_extract_golden.py`。搬后对照由 `tests/test_api_service_extract_golden.py` 重算,通过。 + +构造语句三栏(17 个测试文件和 2 个生产文件,每处相同): + +| 原值 | 新值 | 原因 | +| --- | --- | --- | +| `JyotishAPIHandler.__new__(JyotishAPIHandler)` | `JyotishComputeService()` | 计算类可以正常实例化,不必再绕过 HTTP 构造 | + +`tests/test_active_rectification_api.py` 另有三处: + +| 原值 | 新值 | 原因 | +| --- | --- | --- | +| 补丁打在 `JyotishAPIHandler` | 补丁打在 `JyotishComputeService` | 实例已是计算类,补丁必须打在实例的类上。断言未改 | + +未改的 HTTP 侧伪造(5 处,合同新基线就是这 5 处):`test_api_server_security.py`、`test_api_async_job_contract.py`、`test_runtime_security_p0.py`、`test_dynamic_rectification_scoring.py`、`test_professional_report_reference_api.py`。其中安全测试文件 `git diff` 为空。 + +## 命令和结果 + +| 检查 | 命令 | 结果数字 | +| --- | --- | --- | +| 搬前 golden 连跑两次 | 基线上采集 MCP / HTTP / 报告裁剪 / 证据助手 | 8 个哈希两次相同 | +| 搬后 golden | `pytest tests/test_api_service_extract_golden.py`(含在下面那条里) | 通过,与搬前哈希一致 | +| 任务书点名测试 + 改过的测试 + 两个调用了采集入口的测试 | `pytest` 上述文件 `-q --tb=line` | **383 通过,1 失败**,约 1046 秒 | +| 方法体对比 | 基线文件与工作树 `ast.dump` | 214 对 214,不一致 0 | +| 反向:计算类临时写 `self.headers` | `test_compute_service_methods_do_not_touch_http` | 失败后已撤回 | +| 反向:`scripts/` 临时加一处伪造标记 | `test_scripts_handler_forgeries_must_not_grow` | 1 > 0,失败后已撤回 | +| 安全测试文件 | `git diff -- tests/test_api_server_security.py` | 空 | +| 生产伪造 | 在 `scripts/**/*.py` 搜伪造标记 | **0** | +| 换行数 | 文件内 `\n` 个数 | 10906 → **10920**(上限 11224) | +| 隐私标记 | `pytest tests/test_repo_privacy_markers.py -q` | 退出码 0 | + +搬前哈希(去掉两个墙钟字段后的规范 JSON): + +| 案例 | sha256 | 字节 | +| --- | --- | ---: | +| mcp.career | `ad38a539cf9036143333acd538567b7eb94528d537c46ef326905ebc47527a05` | 543868 | +| http.career | `6a6ca421d942e2577e587647324ef9521ea14841a74a6850c11f629df4d9ba96` | 420555 | +| mcp.wealth | `9aaacf64d08f17bca2d3f6b020b1dbb1ec07eaee7727b04bba90430fcbf594d3` | 542629 | +| http.wealth | `f22368bbde38b76dc44e2995c34f7809ca96442405c1f13e39f53c4a2dd90a62` | 419316 | +| mcp.relationship | `be70fd603b1de52026ad40f0d6582a27cb8b148018474fcd7d8c4134509b40ae` | 537785 | +| http.relationship | `4405ae821aef81ec03660994d9f8f4104ae3388f339407bfe2721d05d662059b` | 414472 | +| repairs | `41d48a453fbd5d616de083fe07bb8d318c3f60ad2e127764237ef6ae50cc6175` | 74700 | +| evidence | `27975ba40a9ee89fc19543a18cb2ee60fb2477b8a6e8f14004ad411adfc408a2` | 2092 | + +MCP 与 HTTP 彼此不相等:MCP 走 `surface=skill_mcp`,HTTP 方法默认 `api_web`,并且 HTTP 方法还会多挂一层咨询全文。这是原来的两条路径,不是这次拆类造成的。每条路径搬前搬后各自一致。 + +## 那 1 条失败 + +`tests/test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow` 期望时间窗 `05:07–05:08`,实际得到 `07:00–07:02`。把该测试文件还原到基线后再跑,失败内容相同。不是拆类引入的,也不是「用到了 HTTP」。按任务书,这种红不回退该文件。断言没改。同文件其余测试在本轮通过。 + +## 偏离 + +- 任务书交付物写了「删两处死代码」。硬红线写明零引用函数只记进度、本单不删。`_strict_adjudication_bundle_from_contract` 与 `_high_rigor_job_path` 仍只在定义处出现,没有删。 +- 后续「把咨询闭包搬出文件、改依赖方向」没做。那一步要改安全测试的补丁落点,任务书明确不做。 +- `AGENTS.md` §6 没有写死伪造数字 31 或 2,所以没改措辞。 +- `skills/jyotish-vedic-astrology/scripts/` 里的同名副本没改。任务书点名的是 `scripts/`。合同计数也不含技能副本。 +- 另有若干 `object.__new__(JyotishAPIHandler)`,不在合同计数里,调用的是计算方法,经继承仍可用。本单没改。 +- 没跑:Python 快速门、全量 pytest、前端 `npm test`、`test:db`。没动数据库,也没对生产或 staging 做压测或登录。 diff --git a/scripts/capture_report_blocked_repairs_golden.py b/scripts/capture_report_blocked_repairs_golden.py index 8f82cc4d..71203cff 100644 --- a/scripts/capture_report_blocked_repairs_golden.py +++ b/scripts/capture_report_blocked_repairs_golden.py @@ -13,7 +13,7 @@ ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) from consultation_plan_contract import PLAN_VERSION, _ROUTE_CONTRACTS # noqa: E402 -from jyotish_api_server import JyotishAPIHandler # noqa: E402 +from jyotish_api_server import JyotishComputeService # noqa: E402 BIRTH = { "year": 1993, @@ -55,8 +55,8 @@ CONSUMER_KEEP = ( CHART_KEEP = ("success", "ascendant", "planets", "houses", "dasha") -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() def _body(theme: str) -> dict: diff --git a/scripts/consultation_workflow_service.py b/scripts/consultation_workflow_service.py index faf9b4a3..c5364a5b 100644 --- a/scripts/consultation_workflow_service.py +++ b/scripts/consultation_workflow_service.py @@ -15,9 +15,9 @@ for path in (ROOT, SCRIPTS_DIR): def execute_consultation_workflow(body: dict[str, Any], *, surface: str = "api") -> dict[str, Any]: - from jyotish_api_server import JyotishAPIHandler, execute_consultation_workflow as _execute + from jyotish_api_server import JyotishComputeService, execute_consultation_workflow as _execute - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() return _execute(handler, body=body, surface=surface) diff --git a/scripts/jyotish_api_server.py b/scripts/jyotish_api_server.py index 8a4c53d2..908d7796 100644 --- a/scripts/jyotish_api_server.py +++ b/scripts/jyotish_api_server.py @@ -3120,76 +3120,8 @@ class RateLimited(RuntimeError): """Client exceeded the local fixed-window request budget.""" -class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryMixin): - server_version = 'JyotishAPI/6.9.14' - - def _json(self, data, status=200, extra_headers=None): - self.send_response(status) - self.send_header('Content-Type', 'application/json; charset=utf-8') - self._send_cors_headers() - self.send_header('X-Content-Type-Options', 'nosniff') - self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') - self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') - self.send_header('Vary', 'Origin') - for key, value in (extra_headers or {}).items(): - self.send_header(key, value) - self.end_headers() - self.wfile.write(json.dumps(data, ensure_ascii=False, default=str).encode()) - - def _error_json(self, message, status=500, error_code='ERR_INTERNAL', extra_headers=None): - self._json({'success': False, 'error': message, 'error_code': error_code}, status, extra_headers=extra_headers) - - def _html(self, content, status=200): - encoded = content.encode('utf-8') - self.send_response(status) - self.send_header('Content-Type', 'text/html; charset=utf-8') - self._send_cors_headers() - self.send_header('X-Content-Type-Options', 'nosniff') - self.send_header('Cache-Control', 'no-store') - self.send_header('Content-Length', str(len(encoded))) - self.end_headers() - self.wfile.write(encoded) - - def _send_cors_headers(self): - origin = self.headers.get('Origin') - allowed = getattr(self.server, 'allowed_origins', DEFAULT_ALLOWED_ORIGINS) - if origin in allowed: - self.send_header('Access-Control-Allow-Origin', origin) - - def _enforce_request_security(self, *, require_json=False): - origin = self.headers.get('Origin') - allowed = getattr(self.server, 'allowed_origins', DEFAULT_ALLOWED_ORIGINS) - if origin and origin not in allowed: - raise Forbidden('Origin is not allowed') - host = (self.headers.get('Host') or '').split(':', 1)[0].strip('[]').lower() - allowed_hosts = getattr(self.server, 'allowed_hosts', DEFAULT_ALLOWED_HOSTS) - if host and host not in allowed_hosts: - raise Forbidden('Host is not allowed') - if require_json: - content_type = (self.headers.get('Content-Type') or '').split(';', 1)[0].strip().lower() - if content_type != 'application/json': - raise UnsupportedMediaType('Content-Type must be application/json') - if urlparse(self.path).path.startswith('/api/'): - client = getattr(self, 'client_address', ('unknown',))[0] - enforce_rate_limit(str(client)) - - def _job_access_token(self): - authorization = self.headers.get('Authorization') or '' - scheme, _, token = authorization.partition(' ') - return token.strip() if scheme.lower() == 'bearer' else '' - - def _require_dynamic_rectification_token(self): - configured = os.environ.get('JYOTISH_DYNAMIC_RECTIFICATION_TOKEN', '').strip() - if not configured: - service_role = os.environ.get('SUPABASE_SERVICE_ROLE_KEY', '').strip() - if service_role: - configured = hashlib.sha256( - f'jyotisha-dynamic-rectification-v1:{service_role}'.encode('utf-8') - ).hexdigest() - supplied = self._job_access_token() - matches = secrets.compare_digest(supplied, configured) - if not configured or not matches: - raise Forbidden('Dynamic rectification server token is missing or invalid') +class JyotishComputeService(VedastroEvidenceMixin, SynastryMixin): + """纯计算方法,不碰 HTTP。""" def _vedastro_status(self): adapter = _load_local_module('vedastro_service_adapter') @@ -3237,414 +3169,9 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM 'boundary': 'VedAstro is optional external timing evidence; local Jyotish gates remain authoritative.', } - def do_OPTIONS(self): - try: - self._enforce_request_security() - self._json({}) - except RateLimited as exc: - self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') - except Forbidden as exc: - self._error_json(str(exc), 403, 'ERR_FORBIDDEN') - def do_GET(self): - path = urlparse(self.path).path - try: - self._enforce_request_security() - if path == '/': - page = Path(REPO_ROOT) / 'web' / 'index.html' - if not page.is_file(): - self._error_json('Home page unavailable', 404, 'ERR_NOT_FOUND') - else: - self._html(page.read_text(encoding='utf-8')) - elif path == '/evidence': - page = Path(REPO_ROOT) / 'web' / 'evidence_packet.html' - if not page.is_file(): - self._error_json('Evidence Packet page unavailable', 404, 'ERR_NOT_FOUND') - else: - self._html(page.read_text(encoding='utf-8')) - elif path == '/rectification': - page = Path(REPO_ROOT) / 'web' / 'rectification.html' - if not page.is_file(): - self._error_json('Rectification page unavailable', 404, 'ERR_NOT_FOUND') - else: - self._html(page.read_text(encoding='utf-8')) - elif path == '/api/health': - swisseph_available = False - swisseph_version = None - try: - import swisseph as swe - swisseph_available = True - swisseph_version = getattr(swe, 'version', None) or getattr(swe, '__version__', None) - except Exception: - swisseph_available = False - swisseph_version = None - self._json({ - 'status': 'ok', - 'version': __version__, - 'git_commit': os.environ.get('GITHUB_SHA') or 'unknown', - 'swisseph_available': swisseph_available, - 'swisseph_version': swisseph_version, - 'ayanamsa_default': DEFAULT_AYANAMSA_NAME, - 'modules': 'Chart/KP/Synastry/Prashna/Remedies/Dasha/Varga/Jaimini/Ashtakavarga/Shadbala/Yoga/Aspects/Tajika/Muhurta/BhavaChalit/BhavaBala/Sudarshana/Nakshatra/Transit/RectificationGate/CaseValidation/DivisionalYoga/Kakshya', - 'async_job_runtime': async_job_runtime_status(), - 'vedastro': self._vedastro_status(), - }) - elif path == '/api/rectification/v5/versions': - self._json(self._compute_rectification_v5_versions()) - elif path == '/api/cities': - self._json(list(CITY_DB.keys())) - elif path == '/api/capability_audit': - self._json(self._capability_audit()) - elif path == '/api/technique_catalog': - self._json(self._technique_catalog()) - elif path == '/api/vedastro/status': - self._json(self._vedastro_status()) - elif path == '/api/vedastro_gateway/status': - self._json(self._compute_vedastro_gateway_status()) - elif path == '/api/vedastro_gateway/archives': - self._json(self._compute_vedastro_gateway_archives()) - elif path.startswith('/api/vedastro_gateway/jobs/'): - job_id = path.rsplit('/', 1)[-1] - result = self._compute_vedastro_gateway_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(result) - elif path.startswith('/api/chart/jobs/'): - job_id = path.rsplit('/', 1)[-1] - result = self._get_chart_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(result) - elif path.startswith('/api/high_rigor_workflow/jobs/'): - job_id = path.rsplit('/', 1)[-1] - result = self._get_high_rigor_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(result) - elif path.startswith('/api/evidence_packet/chart/'): - job_id = path.rsplit('/', 1)[-1] - result = self._get_chart_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(build_evidence_packet_view(result)) - elif path.startswith('/api/evidence_packet/high_rigor_workflow/'): - job_id = path.rsplit('/', 1)[-1] - result = self._get_high_rigor_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(build_evidence_packet_view(result)) - elif path == '/api/real_case_revalidation': - self._json(self._real_case_revalidation()) - else: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - except RateLimited as exc: - self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') - except (Forbidden, JobAccessDenied) as exc: - self._error_json(str(exc), 403, 'ERR_FORBIDDEN') - except Exception: - import logging - logging.exception("[api_server] GET request failed for %s", path) - self._error_json('Internal server error', 500, 'ERR_INTERNAL') - def do_POST(self): - path = urlparse(self.path).path - compute_slot = None - try: - self._enforce_request_security(require_json=True) - body = self._read_json_body() - # Warm chart-save must not take a slot (BUG-1302). Chart overview - # gating is off unless JYOTISH_GATE_CHART_OVERVIEW=1. Both live in - # the gate module so this file only keeps the call. - compute_slot = acquire_heavy_compute_slot_for_request(path, body) - if path == '/api/location/resolve': - city = str(body.get('city') or '').strip() - city_aliases = {'beijing': '北京', 'shanghai': '上海', 'guangzhou': '广州', 'shenzhen': '深圳'} - query = city_aliases.get(city.casefold(), city) - matched = next((name for name in CITY_DB if name.casefold() == query.casefold()), None) - if not matched: - self._error_json('City not found in local city database', 404, 'ERR_CITY_NOT_FOUND') - else: - lat, lon, tz = CITY_DB[matched] - self._json({'status': 'local_city_match', 'city': matched, 'lat': lat, 'lon': lon, 'tz': tz}) - elif path == '/api/location/timezone': - try: - self._json(resolve_location_timezone_payload(body)) - except (KeyError, TypeError, ValueError) as exc: - self._error_json(str(exc), 400, 'ERR_INVALID_LOCATION_TIMEZONE_REQUEST') - elif path == '/api/chart': - result = self._compute_chart(body) - self._json(result) - elif path == '/api/qizheng': - qizheng = _load_local_module('qizheng_chart_engine') - try: - result = qizheng.calculate_qizheng_chart(body) - except qizheng.QizhengChartError as exc: - raise BadRequest(str(exc)) from exc - self._json(result) - elif path == '/api/western': - western = _load_local_module('western_chart_engine') - try: - result = western.build_tropical_natal_from_request(body) - except (KeyError, TypeError, ValueError) as exc: - raise BadRequest(str(exc)) from exc - self._json(result) - elif path == '/api/ephemeris_events': - ephemeris = _load_local_module('ephemeris_events') - try: - result = ephemeris.build_ephemeris_events(body) - except ephemeris.EphemerisEventsError as exc: - raise BadRequest(str(exc)) from exc - self._json(result) - elif path == '/api/birth_sky': - birth_sky = _load_local_module('birth_sky') - try: - result = birth_sky.compute_birth_sky_from_request(body) - except birth_sky.BirthSkyError as exc: - raise BadRequest(str(exc)) from exc - self._json(result) - elif path == '/api/declared_window_chart': - result = self._compute_declared_window_chart(body) - self._json(result) - elif path == '/api/daily_guidance': - result = _load_local_module('daily_guidance_service').build_daily_guidance(body) - self._json(result) - elif path == '/api/remedies': - result = self._compute_remedies(body) - self._json(result) - elif path == '/api/kp': - result = self._compute_kp(body) - self._json(result) - elif path == '/api/prashna': - result = self._compute_prashna(body) - self._json(result) - elif path == '/api/synastry': - result = self._compute_synastry(body) - self._json(result) - elif path == '/api/dasha': - result = self._compute_dasha_system(body) - self._json(result) - elif path == '/api/dasha/chara': - result = self._compute_chara_dasha(body) - self._json(result) - elif path == '/api/sade_sati': - result = self._compute_sade_sati(body) - self._json(result) - elif path == '/api/pancha_mahapurusha': - result = self._compute_pmc(body) - self._json(result) - elif path == '/api/career': - result = self._compute_career(body) - self._json(result) - elif path == '/api/relationship': - result = self._compute_relationship(body) - self._json(result) - elif path == '/api/vedastro/range_scan': - result = self._compute_vedastro_range_scan(body) - self._json(result) - elif path == '/api/vedastro_gateway/run': - result = self._compute_vedastro_gateway_run(body) - self._json(result) - elif path == '/api/vedastro_gateway/enqueue': - result = self._compute_vedastro_gateway_enqueue(body) - self._json(result) - elif path.startswith('/api/vedastro_gateway/jobs/') and path.endswith('/run'): - job_id = path.split('/')[-2] - result = self._compute_vedastro_gateway_run_job(job_id) - if result is None: - self._error_json('Not found', 404, 'ERR_NOT_FOUND') - else: - self._json(result) - elif path == '/api/professional_reading': - result = self._compute_professional_reading(body) - self._json(result) - elif path == '/api/professional_report_reference': - reference_module = _load_local_module('professional_report_reference') - try: - result = reference_module.build_professional_report_reference(self, body) - except reference_module.ProfessionalReportReferenceInputError as exc: - raise BadRequest(str(exc)) from exc - self._json(result) - elif path == '/api/import_chart': - result = self._import_chart_text(body) - self._json(result) - elif path == '/api/report_artifact': - result = self._compute_report_artifact(body) - self._json(result) - elif path == '/api/oracle_evidence': - result = self._compute_oracle_evidence(body) - self._json(result) - elif path == '/api/annual': - result = self._compute_annual(body) - self._json(result) - elif path == '/api/tajika': - result = self._compute_tajika(body) - self._json(result) - elif path == '/api/muhurta': - result = self._compute_muhurta(body) - self._json(result) - elif path == '/api/panchanga_range': - result = self._compute_panchanga_range(body) - self._json(result) - elif path == '/api/bhava_chalit': - result = self._compute_bhava_chalit(body) - self._json(result) - elif path == '/api/sudarshana': - result = self._compute_sudarshana(body) - self._json(result) - elif path == '/api/nakshatra_full': - result = self._compute_nakshatra_full(body) - self._json(result) - elif path == '/api/varga_full': - result = self._compute_varga_full(body) - self._json(result) - elif path == '/api/jaimini': - result = self._compute_jaimini(body) - self._json(result) - elif path == '/api/ashtakavarga': - result = self._compute_ashtakavarga(body) - self._json(result) - elif path == '/api/shadbala': - result = self._compute_shadbala(body) - self._json(result) - elif path == '/api/yogas': - result = self._compute_yogas_api(body) - self._json(result) - elif path == '/api/aspects': - result = self._compute_aspects(body) - self._json(result) - elif path == '/api/rectification/questionnaire': - self._json(build_rectification_questionnaire(body)) - elif path == '/api/rectification/sensitivity_scan': - uncertainty = int(body.get('time_uncertainty_minutes') or 30) - step_minutes = int(body.get('step_minutes') or (5 if uncertainty > 15 else 1)) - self._json(scan_candidate_times( - body, - uncertainty_minutes=uncertainty, - step_minutes=step_minutes, - )) - elif path == '/api/rectification/answers': - questionnaire = body.get('questionnaire') - answers = body.get('answers') - if not isinstance(questionnaire, dict) or not isinstance(answers, dict): - raise BadRequest('questionnaire and answers must be JSON objects') - self._json(score_rectification_answers(questionnaire, answers)) - elif path == '/api/rectification_gate': - result = self._compute_rectification_gate(body) - self._json(result) - elif path == '/api/active_rectification_questions': - result = self._compute_active_rectification_questions(body) - self._json(result) - elif path == '/api/active_rectification_score': - result = self._compute_active_rectification_score(body) - self._json(result) - elif path == '/api/active_rectification_events': - result = self._compute_active_rectification_events(body) - self._json(result) - elif path == '/api/active_rectification_events_v4': - result = self._compute_active_rectification_events_v4(body) - self._json(result) - elif path == '/api/rectification/v5/varga-segments': - _load_local_module('rectification_varga_segments_api').handle_varga_segments(self, body) - elif path == '/api/rectification/v5/candidate-features': - self._json(self._compute_rectification_v5_candidate_features(body)) - elif path == '/api/rectification/v5/score': - self._json(self._compute_rectification_v5_score(body)) - elif path == '/api/rectification/v5/diagnostics': - self._json(self._compute_rectification_v5_diagnostics(body)) - elif path == '/api/rectification/v5/vedastro-validate': - self._json(self._compute_rectification_v5_vedastro_validate(body)) - elif path == '/api/rectification/v5/range_reading': - self._json(self._compute_rectification_v5_range_reading(body)) - elif path == '/api/rectification/v5/block_scan': - self._json(self._compute_rectification_v5_block_scan(body)) - elif path == '/api/rectification/v5/versions': - self._json(self._compute_rectification_v5_versions()) - elif path == '/api/dynamic_rectification_opportunities': - result = self._compute_dynamic_rectification_opportunities(body) - self._json(result) - elif path == '/api/dynamic_rectification_score': - result = self._compute_dynamic_rectification_score(body) - self._json(result) - elif path == '/api/case_validation': - result = self._compute_case_validation(body) - self._json(result) - elif path == '/api/divisional_yoga': - result = self._compute_divisional_yoga(body) - self._json(result) - elif path == '/api/deep_varga_avastha': - result = self._compute_deep_varga_avastha(body) - self._json(result) - elif path == '/api/kakshya': - result = self._compute_kakshya(body) - self._json(result) - elif path == '/api/bhava_bala': - result = self._compute_bhava_bala_api(body) - self._json(result) - elif path == '/api/transit': - result = self._compute_transit_triggers(body) - self._json(result) - elif path == '/api/thematic_report': - result = self._compute_thematic_report(body) - self._json(result) - elif path == '/api/high_rigor_workflow': - result = self._compute_high_rigor_workflow(body) - self._json(result) - elif path == '/api/consultation_workflow': - result = self._compute_consultation_workflow(body) - self._json(result) - elif path == '/api/technique_example': - result = self._compute_technique_example(body) - self._json(result) - else: - self._error_json(f'Unknown endpoint: {path}', 404, 'ERR_NOT_FOUND') - except HeavyComputeBusy as exc: - self._error_json( - str(exc), - 429, - exc.error_code, - extra_headers={'Retry-After': str(exc.retry_after_seconds)}, - ) - except RateLimited as exc: - self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') - except BadRequest as e: - self._error_json(str(e), 400, 'ERR_BAD_REQUEST') - except Forbidden as exc: - self._error_json(str(exc), 403, 'ERR_FORBIDDEN') - except UnsupportedMediaType as exc: - self._error_json(str(exc), 415, 'ERR_UNSUPPORTED_MEDIA_TYPE') - except JobQueueFull as exc: - self._error_json(str(exc), 503, 'ERR_JOB_QUEUE_FULL') - except Exception: - import logging - logging.exception("[api_server] request failed for %s", path) - self._error_json('Internal server error', 500, 'ERR_INTERNAL') - finally: - release_heavy_compute_slot(compute_slot) - def _read_json_body(self): - raw_length = self.headers.get('Content-Length', '0') - try: - length = int(raw_length) - except ValueError as e: - raise BadRequest('Invalid Content-Length') from e - if length < 0: - raise BadRequest('Invalid Content-Length') - if length > MAX_REQUEST_BYTES: - raise BadRequest(f'Request body too large; max {MAX_REQUEST_BYTES} bytes') - if length == 0: - return {} - try: - body = json.loads(self.rfile.read(length)) - except json.JSONDecodeError as e: - raise BadRequest('Invalid JSON body') from e - if not isinstance(body, dict): - raise BadRequest('JSON body must be an object') - return body def _get_int(self, body, key, default, min_value=None, max_value=None): value = body.get(key, default) @@ -4638,15 +4165,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM _submit_background_job(_run_job) return record - def _get_high_rigor_job(self, job_id): - return _load_high_rigor_job_record(job_id, access_token=self._job_access_token()) - def _get_chart_job(self, job_id): - return _load_async_job_record( - _API_CHART_CACHE_SCOPE, - job_id, - access_token=self._job_access_token(), - ) def _high_rigor_birth_payload(self, body): required = ('year', 'month', 'day', 'hour', 'minute', 'lat', 'lon') @@ -9086,104 +8605,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM **score_candidates(self._rectification_v5_request(body)), } - def _compute_dynamic_rectification_opportunities(self, body): - self._require_dynamic_rectification_token() - allowed_fields = { - 'case_id', 'birth_date', 'as_of_date', 'start_time', 'end_time', - 'lat', 'lon', 'tz', 'candidate_model', 'evidence', - 'events', - 'dismissed_opportunity_ids', 'question_fingerprints', - 'partition_fingerprints', 'recent_ranges', - } - normalized = self._dynamic_rectification_base(body, allowed_fields, 'opportunity') - case_id = normalized.get('case_id') - as_of_date = normalized.get('as_of_date') - if not isinstance(case_id, str) or not case_id.strip(): - raise BadRequest('case_id must be a non-empty string') - if not isinstance(as_of_date, str): - raise BadRequest('as_of_date must be YYYY-MM-DD') - try: - datetime.strptime(as_of_date, '%Y-%m-%d') - except ValueError as exc: - raise BadRequest('as_of_date must be YYYY-MM-DD') from exc - for key in ('evidence', 'dismissed_opportunity_ids', 'question_fingerprints', 'partition_fingerprints', 'recent_ranges'): - if not isinstance(normalized.get(key), list): - raise BadRequest(f'{key} must be an array') - if any(not isinstance(item, str) or not item for key in ('dismissed_opportunity_ids', 'question_fingerprints', 'partition_fingerprints') for item in normalized[key]): - raise BadRequest('dynamic rectification fingerprints and IDs must be non-empty strings') - for item in normalized['recent_ranges']: - if not isinstance(item, dict) or set(item) != {'start_time', 'end_time'}: - raise BadRequest('recent_ranges must contain only start_time and end_time') - try: - datetime.strptime(item['start_time'], '%H:%M') - datetime.strptime(item['end_time'], '%H:%M') - except (KeyError, TypeError, ValueError) as exc: - raise BadRequest('recent_ranges must contain valid HH:MM times') from exc - if normalized.get('candidate_model') is not None and not isinstance(normalized['candidate_model'], dict): - raise BadRequest('candidate_model must be an object') - # Older stored clients did not send historical events. Treat omission as - # an empty evidence set so resuming an unfinished case never fails just - # because its cached request predates event-aware question selection. - events = normalized.get('events', []) - if not isinstance(events, list) or len(events) > 6: - raise BadRequest('events must be an array with at most 6 items') - parsed_birth_date = datetime.strptime(normalized['birth_date'], '%Y-%m-%d') - allowed_domains = {'education', 'relocation', 'relationship', 'career', 'finance', 'health_pressure'} - formats = {'year': '%Y', 'month': '%Y-%m', 'day': '%Y-%m-%d'} - normalized_events = [] - for raw_event in events: - if not isinstance(raw_event, dict) or set(raw_event) != {'id', 'domain', 'date', 'precision'}: - raise BadRequest('each event must contain only id, domain, date, and precision') - try: - event_id = str(uuid.UUID(str(raw_event['id']))) - except (ValueError, TypeError, AttributeError) as exc: - raise BadRequest('event id must be a UUID') from exc - domain = raw_event['domain'] - precision = raw_event['precision'] - event_date = raw_event['date'] - if domain not in allowed_domains: - raise BadRequest('event domain is unsupported') - if precision not in formats or not isinstance(event_date, str): - raise BadRequest('event precision is unsupported') - try: - parsed_event_date = datetime.strptime(event_date, formats[precision]) - except ValueError as exc: - raise BadRequest('event date does not match its precision') from exc - if parsed_event_date.year < parsed_birth_date.year or parsed_event_date > datetime.now(): - raise BadRequest('event date must be between birth and today') - normalized_events.append({ - 'id': event_id, - 'domain': domain, - 'date': event_date, - 'precision': precision, - }) - normalized['events'] = normalized_events - module = _load_local_module('dynamic_rectification') - try: - result = module.build_difference_packet(normalized) - except (KeyError, TypeError, ValueError) as exc: - raise BadRequest(str(exc)) from exc - return {'success': True, 'endpoint': 'dynamic_rectification_opportunities', **result} - def _compute_dynamic_rectification_score(self, body): - self._require_dynamic_rectification_token() - allowed_fields = { - 'birth_date', 'start_time', 'end_time', 'lat', 'lon', 'tz', 'choice_evidence', - } - normalized = self._dynamic_rectification_base(body, allowed_fields, 'score') - evidence = normalized.get('choice_evidence') - if not isinstance(evidence, list): - raise BadRequest('choice_evidence must be an array') - if any(isinstance(item, dict) and 'option_id' in item for item in evidence): - raise BadRequest('option_id is client-owned and cannot be scored') - module = _load_local_module('dynamic_rectification') - try: - result = module.score_choice_evidence(normalized) - except (KeyError, TypeError, ValueError) as exc: - raise BadRequest(str(exc)) from exc - result['can_apply'] = False - result.setdefault('reasons', []).append('vedastro_validation_required') - return {'success': True, 'endpoint': 'dynamic_rectification_score', **result} def _compute_case_validation(self, body): planets, _, _ = self._normalized_planets_from_body(body) @@ -10822,6 +10244,598 @@ class JyotishAPIHandler(BaseHTTPRequestHandler, VedastroEvidenceMixin, SynastryM ] +class JyotishAPIHandler(BaseHTTPRequestHandler, JyotishComputeService): + """HTTP 入口,计算沿用 JyotishComputeService。""" + + server_version = 'JyotishAPI/6.9.14' + + def _json(self, data, status=200, extra_headers=None): + self.send_response(status) + self.send_header('Content-Type', 'application/json; charset=utf-8') + self._send_cors_headers() + self.send_header('X-Content-Type-Options', 'nosniff') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') + self.send_header('Vary', 'Origin') + for key, value in (extra_headers or {}).items(): + self.send_header(key, value) + self.end_headers() + self.wfile.write(json.dumps(data, ensure_ascii=False, default=str).encode()) + + def _error_json(self, message, status=500, error_code='ERR_INTERNAL', extra_headers=None): + self._json({'success': False, 'error': message, 'error_code': error_code}, status, extra_headers=extra_headers) + + def _html(self, content, status=200): + encoded = content.encode('utf-8') + self.send_response(status) + self.send_header('Content-Type', 'text/html; charset=utf-8') + self._send_cors_headers() + self.send_header('X-Content-Type-Options', 'nosniff') + self.send_header('Cache-Control', 'no-store') + self.send_header('Content-Length', str(len(encoded))) + self.end_headers() + self.wfile.write(encoded) + + def _send_cors_headers(self): + origin = self.headers.get('Origin') + allowed = getattr(self.server, 'allowed_origins', DEFAULT_ALLOWED_ORIGINS) + if origin in allowed: + self.send_header('Access-Control-Allow-Origin', origin) + + def _enforce_request_security(self, *, require_json=False): + origin = self.headers.get('Origin') + allowed = getattr(self.server, 'allowed_origins', DEFAULT_ALLOWED_ORIGINS) + if origin and origin not in allowed: + raise Forbidden('Origin is not allowed') + host = (self.headers.get('Host') or '').split(':', 1)[0].strip('[]').lower() + allowed_hosts = getattr(self.server, 'allowed_hosts', DEFAULT_ALLOWED_HOSTS) + if host and host not in allowed_hosts: + raise Forbidden('Host is not allowed') + if require_json: + content_type = (self.headers.get('Content-Type') or '').split(';', 1)[0].strip().lower() + if content_type != 'application/json': + raise UnsupportedMediaType('Content-Type must be application/json') + if urlparse(self.path).path.startswith('/api/'): + client = getattr(self, 'client_address', ('unknown',))[0] + enforce_rate_limit(str(client)) + + def _job_access_token(self): + authorization = self.headers.get('Authorization') or '' + scheme, _, token = authorization.partition(' ') + return token.strip() if scheme.lower() == 'bearer' else '' + + def _require_dynamic_rectification_token(self): + configured = os.environ.get('JYOTISH_DYNAMIC_RECTIFICATION_TOKEN', '').strip() + if not configured: + service_role = os.environ.get('SUPABASE_SERVICE_ROLE_KEY', '').strip() + if service_role: + configured = hashlib.sha256( + f'jyotisha-dynamic-rectification-v1:{service_role}'.encode('utf-8') + ).hexdigest() + supplied = self._job_access_token() + matches = secrets.compare_digest(supplied, configured) + if not configured or not matches: + raise Forbidden('Dynamic rectification server token is missing or invalid') + + def do_OPTIONS(self): + try: + self._enforce_request_security() + self._json({}) + except RateLimited as exc: + self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') + except Forbidden as exc: + self._error_json(str(exc), 403, 'ERR_FORBIDDEN') + + def do_GET(self): + path = urlparse(self.path).path + try: + self._enforce_request_security() + if path == '/': + page = Path(REPO_ROOT) / 'web' / 'index.html' + if not page.is_file(): + self._error_json('Home page unavailable', 404, 'ERR_NOT_FOUND') + else: + self._html(page.read_text(encoding='utf-8')) + elif path == '/evidence': + page = Path(REPO_ROOT) / 'web' / 'evidence_packet.html' + if not page.is_file(): + self._error_json('Evidence Packet page unavailable', 404, 'ERR_NOT_FOUND') + else: + self._html(page.read_text(encoding='utf-8')) + elif path == '/rectification': + page = Path(REPO_ROOT) / 'web' / 'rectification.html' + if not page.is_file(): + self._error_json('Rectification page unavailable', 404, 'ERR_NOT_FOUND') + else: + self._html(page.read_text(encoding='utf-8')) + elif path == '/api/health': + swisseph_available = False + swisseph_version = None + try: + import swisseph as swe + swisseph_available = True + swisseph_version = getattr(swe, 'version', None) or getattr(swe, '__version__', None) + except Exception: + swisseph_available = False + swisseph_version = None + self._json({ + 'status': 'ok', + 'version': __version__, + 'git_commit': os.environ.get('GITHUB_SHA') or 'unknown', + 'swisseph_available': swisseph_available, + 'swisseph_version': swisseph_version, + 'ayanamsa_default': DEFAULT_AYANAMSA_NAME, + 'modules': 'Chart/KP/Synastry/Prashna/Remedies/Dasha/Varga/Jaimini/Ashtakavarga/Shadbala/Yoga/Aspects/Tajika/Muhurta/BhavaChalit/BhavaBala/Sudarshana/Nakshatra/Transit/RectificationGate/CaseValidation/DivisionalYoga/Kakshya', + 'async_job_runtime': async_job_runtime_status(), + 'vedastro': self._vedastro_status(), + }) + elif path == '/api/rectification/v5/versions': + self._json(self._compute_rectification_v5_versions()) + elif path == '/api/cities': + self._json(list(CITY_DB.keys())) + elif path == '/api/capability_audit': + self._json(self._capability_audit()) + elif path == '/api/technique_catalog': + self._json(self._technique_catalog()) + elif path == '/api/vedastro/status': + self._json(self._vedastro_status()) + elif path == '/api/vedastro_gateway/status': + self._json(self._compute_vedastro_gateway_status()) + elif path == '/api/vedastro_gateway/archives': + self._json(self._compute_vedastro_gateway_archives()) + elif path.startswith('/api/vedastro_gateway/jobs/'): + job_id = path.rsplit('/', 1)[-1] + result = self._compute_vedastro_gateway_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(result) + elif path.startswith('/api/chart/jobs/'): + job_id = path.rsplit('/', 1)[-1] + result = self._get_chart_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(result) + elif path.startswith('/api/high_rigor_workflow/jobs/'): + job_id = path.rsplit('/', 1)[-1] + result = self._get_high_rigor_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(result) + elif path.startswith('/api/evidence_packet/chart/'): + job_id = path.rsplit('/', 1)[-1] + result = self._get_chart_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(build_evidence_packet_view(result)) + elif path.startswith('/api/evidence_packet/high_rigor_workflow/'): + job_id = path.rsplit('/', 1)[-1] + result = self._get_high_rigor_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(build_evidence_packet_view(result)) + elif path == '/api/real_case_revalidation': + self._json(self._real_case_revalidation()) + else: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + except RateLimited as exc: + self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') + except (Forbidden, JobAccessDenied) as exc: + self._error_json(str(exc), 403, 'ERR_FORBIDDEN') + except Exception: + import logging + logging.exception("[api_server] GET request failed for %s", path) + self._error_json('Internal server error', 500, 'ERR_INTERNAL') + + def do_POST(self): + path = urlparse(self.path).path + compute_slot = None + try: + self._enforce_request_security(require_json=True) + body = self._read_json_body() + # Warm chart-save must not take a slot (BUG-1302). Chart overview + # gating is off unless JYOTISH_GATE_CHART_OVERVIEW=1. Both live in + # the gate module so this file only keeps the call. + compute_slot = acquire_heavy_compute_slot_for_request(path, body) + if path == '/api/location/resolve': + city = str(body.get('city') or '').strip() + city_aliases = {'beijing': '北京', 'shanghai': '上海', 'guangzhou': '广州', 'shenzhen': '深圳'} + query = city_aliases.get(city.casefold(), city) + matched = next((name for name in CITY_DB if name.casefold() == query.casefold()), None) + if not matched: + self._error_json('City not found in local city database', 404, 'ERR_CITY_NOT_FOUND') + else: + lat, lon, tz = CITY_DB[matched] + self._json({'status': 'local_city_match', 'city': matched, 'lat': lat, 'lon': lon, 'tz': tz}) + elif path == '/api/location/timezone': + try: + self._json(resolve_location_timezone_payload(body)) + except (KeyError, TypeError, ValueError) as exc: + self._error_json(str(exc), 400, 'ERR_INVALID_LOCATION_TIMEZONE_REQUEST') + elif path == '/api/chart': + result = self._compute_chart(body) + self._json(result) + elif path == '/api/qizheng': + qizheng = _load_local_module('qizheng_chart_engine') + try: + result = qizheng.calculate_qizheng_chart(body) + except qizheng.QizhengChartError as exc: + raise BadRequest(str(exc)) from exc + self._json(result) + elif path == '/api/western': + western = _load_local_module('western_chart_engine') + try: + result = western.build_tropical_natal_from_request(body) + except (KeyError, TypeError, ValueError) as exc: + raise BadRequest(str(exc)) from exc + self._json(result) + elif path == '/api/ephemeris_events': + ephemeris = _load_local_module('ephemeris_events') + try: + result = ephemeris.build_ephemeris_events(body) + except ephemeris.EphemerisEventsError as exc: + raise BadRequest(str(exc)) from exc + self._json(result) + elif path == '/api/birth_sky': + birth_sky = _load_local_module('birth_sky') + try: + result = birth_sky.compute_birth_sky_from_request(body) + except birth_sky.BirthSkyError as exc: + raise BadRequest(str(exc)) from exc + self._json(result) + elif path == '/api/declared_window_chart': + result = self._compute_declared_window_chart(body) + self._json(result) + elif path == '/api/daily_guidance': + result = _load_local_module('daily_guidance_service').build_daily_guidance(body) + self._json(result) + elif path == '/api/remedies': + result = self._compute_remedies(body) + self._json(result) + elif path == '/api/kp': + result = self._compute_kp(body) + self._json(result) + elif path == '/api/prashna': + result = self._compute_prashna(body) + self._json(result) + elif path == '/api/synastry': + result = self._compute_synastry(body) + self._json(result) + elif path == '/api/dasha': + result = self._compute_dasha_system(body) + self._json(result) + elif path == '/api/dasha/chara': + result = self._compute_chara_dasha(body) + self._json(result) + elif path == '/api/sade_sati': + result = self._compute_sade_sati(body) + self._json(result) + elif path == '/api/pancha_mahapurusha': + result = self._compute_pmc(body) + self._json(result) + elif path == '/api/career': + result = self._compute_career(body) + self._json(result) + elif path == '/api/relationship': + result = self._compute_relationship(body) + self._json(result) + elif path == '/api/vedastro/range_scan': + result = self._compute_vedastro_range_scan(body) + self._json(result) + elif path == '/api/vedastro_gateway/run': + result = self._compute_vedastro_gateway_run(body) + self._json(result) + elif path == '/api/vedastro_gateway/enqueue': + result = self._compute_vedastro_gateway_enqueue(body) + self._json(result) + elif path.startswith('/api/vedastro_gateway/jobs/') and path.endswith('/run'): + job_id = path.split('/')[-2] + result = self._compute_vedastro_gateway_run_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(result) + elif path == '/api/professional_reading': + result = self._compute_professional_reading(body) + self._json(result) + elif path == '/api/professional_report_reference': + reference_module = _load_local_module('professional_report_reference') + try: + result = reference_module.build_professional_report_reference(self, body) + except reference_module.ProfessionalReportReferenceInputError as exc: + raise BadRequest(str(exc)) from exc + self._json(result) + elif path == '/api/import_chart': + result = self._import_chart_text(body) + self._json(result) + elif path == '/api/report_artifact': + result = self._compute_report_artifact(body) + self._json(result) + elif path == '/api/oracle_evidence': + result = self._compute_oracle_evidence(body) + self._json(result) + elif path == '/api/annual': + result = self._compute_annual(body) + self._json(result) + elif path == '/api/tajika': + result = self._compute_tajika(body) + self._json(result) + elif path == '/api/muhurta': + result = self._compute_muhurta(body) + self._json(result) + elif path == '/api/panchanga_range': + result = self._compute_panchanga_range(body) + self._json(result) + elif path == '/api/bhava_chalit': + result = self._compute_bhava_chalit(body) + self._json(result) + elif path == '/api/sudarshana': + result = self._compute_sudarshana(body) + self._json(result) + elif path == '/api/nakshatra_full': + result = self._compute_nakshatra_full(body) + self._json(result) + elif path == '/api/varga_full': + result = self._compute_varga_full(body) + self._json(result) + elif path == '/api/jaimini': + result = self._compute_jaimini(body) + self._json(result) + elif path == '/api/ashtakavarga': + result = self._compute_ashtakavarga(body) + self._json(result) + elif path == '/api/shadbala': + result = self._compute_shadbala(body) + self._json(result) + elif path == '/api/yogas': + result = self._compute_yogas_api(body) + self._json(result) + elif path == '/api/aspects': + result = self._compute_aspects(body) + self._json(result) + elif path == '/api/rectification/questionnaire': + self._json(build_rectification_questionnaire(body)) + elif path == '/api/rectification/sensitivity_scan': + uncertainty = int(body.get('time_uncertainty_minutes') or 30) + step_minutes = int(body.get('step_minutes') or (5 if uncertainty > 15 else 1)) + self._json(scan_candidate_times( + body, + uncertainty_minutes=uncertainty, + step_minutes=step_minutes, + )) + elif path == '/api/rectification/answers': + questionnaire = body.get('questionnaire') + answers = body.get('answers') + if not isinstance(questionnaire, dict) or not isinstance(answers, dict): + raise BadRequest('questionnaire and answers must be JSON objects') + self._json(score_rectification_answers(questionnaire, answers)) + elif path == '/api/rectification_gate': + result = self._compute_rectification_gate(body) + self._json(result) + elif path == '/api/active_rectification_questions': + result = self._compute_active_rectification_questions(body) + self._json(result) + elif path == '/api/active_rectification_score': + result = self._compute_active_rectification_score(body) + self._json(result) + elif path == '/api/active_rectification_events': + result = self._compute_active_rectification_events(body) + self._json(result) + elif path == '/api/active_rectification_events_v4': + result = self._compute_active_rectification_events_v4(body) + self._json(result) + elif path == '/api/rectification/v5/varga-segments': + _load_local_module('rectification_varga_segments_api').handle_varga_segments(self, body) + elif path == '/api/rectification/v5/candidate-features': + self._json(self._compute_rectification_v5_candidate_features(body)) + elif path == '/api/rectification/v5/score': + self._json(self._compute_rectification_v5_score(body)) + elif path == '/api/rectification/v5/diagnostics': + self._json(self._compute_rectification_v5_diagnostics(body)) + elif path == '/api/rectification/v5/vedastro-validate': + self._json(self._compute_rectification_v5_vedastro_validate(body)) + elif path == '/api/rectification/v5/range_reading': + self._json(self._compute_rectification_v5_range_reading(body)) + elif path == '/api/rectification/v5/block_scan': + self._json(self._compute_rectification_v5_block_scan(body)) + elif path == '/api/rectification/v5/versions': + self._json(self._compute_rectification_v5_versions()) + elif path == '/api/dynamic_rectification_opportunities': + result = self._compute_dynamic_rectification_opportunities(body) + self._json(result) + elif path == '/api/dynamic_rectification_score': + result = self._compute_dynamic_rectification_score(body) + self._json(result) + elif path == '/api/case_validation': + result = self._compute_case_validation(body) + self._json(result) + elif path == '/api/divisional_yoga': + result = self._compute_divisional_yoga(body) + self._json(result) + elif path == '/api/deep_varga_avastha': + result = self._compute_deep_varga_avastha(body) + self._json(result) + elif path == '/api/kakshya': + result = self._compute_kakshya(body) + self._json(result) + elif path == '/api/bhava_bala': + result = self._compute_bhava_bala_api(body) + self._json(result) + elif path == '/api/transit': + result = self._compute_transit_triggers(body) + self._json(result) + elif path == '/api/thematic_report': + result = self._compute_thematic_report(body) + self._json(result) + elif path == '/api/high_rigor_workflow': + result = self._compute_high_rigor_workflow(body) + self._json(result) + elif path == '/api/consultation_workflow': + result = self._compute_consultation_workflow(body) + self._json(result) + elif path == '/api/technique_example': + result = self._compute_technique_example(body) + self._json(result) + else: + self._error_json(f'Unknown endpoint: {path}', 404, 'ERR_NOT_FOUND') + except HeavyComputeBusy as exc: + self._error_json( + str(exc), + 429, + exc.error_code, + extra_headers={'Retry-After': str(exc.retry_after_seconds)}, + ) + except RateLimited as exc: + self._error_json(str(exc), 429, 'ERR_RATE_LIMITED') + except BadRequest as e: + self._error_json(str(e), 400, 'ERR_BAD_REQUEST') + except Forbidden as exc: + self._error_json(str(exc), 403, 'ERR_FORBIDDEN') + except UnsupportedMediaType as exc: + self._error_json(str(exc), 415, 'ERR_UNSUPPORTED_MEDIA_TYPE') + except JobQueueFull as exc: + self._error_json(str(exc), 503, 'ERR_JOB_QUEUE_FULL') + except Exception: + import logging + logging.exception("[api_server] request failed for %s", path) + self._error_json('Internal server error', 500, 'ERR_INTERNAL') + finally: + release_heavy_compute_slot(compute_slot) + + def _read_json_body(self): + raw_length = self.headers.get('Content-Length', '0') + try: + length = int(raw_length) + except ValueError as e: + raise BadRequest('Invalid Content-Length') from e + if length < 0: + raise BadRequest('Invalid Content-Length') + if length > MAX_REQUEST_BYTES: + raise BadRequest(f'Request body too large; max {MAX_REQUEST_BYTES} bytes') + if length == 0: + return {} + try: + body = json.loads(self.rfile.read(length)) + except json.JSONDecodeError as e: + raise BadRequest('Invalid JSON body') from e + if not isinstance(body, dict): + raise BadRequest('JSON body must be an object') + return body + + def _get_high_rigor_job(self, job_id): + return _load_high_rigor_job_record(job_id, access_token=self._job_access_token()) + + def _get_chart_job(self, job_id): + return _load_async_job_record( + _API_CHART_CACHE_SCOPE, + job_id, + access_token=self._job_access_token(), + ) + + def _compute_dynamic_rectification_opportunities(self, body): + self._require_dynamic_rectification_token() + allowed_fields = { + 'case_id', 'birth_date', 'as_of_date', 'start_time', 'end_time', + 'lat', 'lon', 'tz', 'candidate_model', 'evidence', + 'events', + 'dismissed_opportunity_ids', 'question_fingerprints', + 'partition_fingerprints', 'recent_ranges', + } + normalized = self._dynamic_rectification_base(body, allowed_fields, 'opportunity') + case_id = normalized.get('case_id') + as_of_date = normalized.get('as_of_date') + if not isinstance(case_id, str) or not case_id.strip(): + raise BadRequest('case_id must be a non-empty string') + if not isinstance(as_of_date, str): + raise BadRequest('as_of_date must be YYYY-MM-DD') + try: + datetime.strptime(as_of_date, '%Y-%m-%d') + except ValueError as exc: + raise BadRequest('as_of_date must be YYYY-MM-DD') from exc + for key in ('evidence', 'dismissed_opportunity_ids', 'question_fingerprints', 'partition_fingerprints', 'recent_ranges'): + if not isinstance(normalized.get(key), list): + raise BadRequest(f'{key} must be an array') + if any(not isinstance(item, str) or not item for key in ('dismissed_opportunity_ids', 'question_fingerprints', 'partition_fingerprints') for item in normalized[key]): + raise BadRequest('dynamic rectification fingerprints and IDs must be non-empty strings') + for item in normalized['recent_ranges']: + if not isinstance(item, dict) or set(item) != {'start_time', 'end_time'}: + raise BadRequest('recent_ranges must contain only start_time and end_time') + try: + datetime.strptime(item['start_time'], '%H:%M') + datetime.strptime(item['end_time'], '%H:%M') + except (KeyError, TypeError, ValueError) as exc: + raise BadRequest('recent_ranges must contain valid HH:MM times') from exc + if normalized.get('candidate_model') is not None and not isinstance(normalized['candidate_model'], dict): + raise BadRequest('candidate_model must be an object') + # Older stored clients did not send historical events. Treat omission as + # an empty evidence set so resuming an unfinished case never fails just + # because its cached request predates event-aware question selection. + events = normalized.get('events', []) + if not isinstance(events, list) or len(events) > 6: + raise BadRequest('events must be an array with at most 6 items') + parsed_birth_date = datetime.strptime(normalized['birth_date'], '%Y-%m-%d') + allowed_domains = {'education', 'relocation', 'relationship', 'career', 'finance', 'health_pressure'} + formats = {'year': '%Y', 'month': '%Y-%m', 'day': '%Y-%m-%d'} + normalized_events = [] + for raw_event in events: + if not isinstance(raw_event, dict) or set(raw_event) != {'id', 'domain', 'date', 'precision'}: + raise BadRequest('each event must contain only id, domain, date, and precision') + try: + event_id = str(uuid.UUID(str(raw_event['id']))) + except (ValueError, TypeError, AttributeError) as exc: + raise BadRequest('event id must be a UUID') from exc + domain = raw_event['domain'] + precision = raw_event['precision'] + event_date = raw_event['date'] + if domain not in allowed_domains: + raise BadRequest('event domain is unsupported') + if precision not in formats or not isinstance(event_date, str): + raise BadRequest('event precision is unsupported') + try: + parsed_event_date = datetime.strptime(event_date, formats[precision]) + except ValueError as exc: + raise BadRequest('event date does not match its precision') from exc + if parsed_event_date.year < parsed_birth_date.year or parsed_event_date > datetime.now(): + raise BadRequest('event date must be between birth and today') + normalized_events.append({ + 'id': event_id, + 'domain': domain, + 'date': event_date, + 'precision': precision, + }) + normalized['events'] = normalized_events + module = _load_local_module('dynamic_rectification') + try: + result = module.build_difference_packet(normalized) + except (KeyError, TypeError, ValueError) as exc: + raise BadRequest(str(exc)) from exc + return {'success': True, 'endpoint': 'dynamic_rectification_opportunities', **result} + + def _compute_dynamic_rectification_score(self, body): + self._require_dynamic_rectification_token() + allowed_fields = { + 'birth_date', 'start_time', 'end_time', 'lat', 'lon', 'tz', 'choice_evidence', + } + normalized = self._dynamic_rectification_base(body, allowed_fields, 'score') + evidence = normalized.get('choice_evidence') + if not isinstance(evidence, list): + raise BadRequest('choice_evidence must be an array') + if any(isinstance(item, dict) and 'option_id' in item for item in evidence): + raise BadRequest('option_id is client-owned and cannot be scored') + module = _load_local_module('dynamic_rectification') + try: + result = module.score_choice_evidence(normalized) + except (KeyError, TypeError, ValueError) as exc: + raise BadRequest(str(exc)) from exc + result['can_apply'] = False + result.setdefault('reasons', []).append('vedastro_validation_required') + return {'success': True, 'endpoint': 'dynamic_rectification_score', **result} + + def _parse_allowed_origins(value): if not value: return DEFAULT_ALLOWED_ORIGINS diff --git a/scripts/research/api_service_extract_golden.py b/scripts/research/api_service_extract_golden.py new file mode 100644 index 00000000..51ccb1e6 --- /dev/null +++ b/scripts/research/api_service_extract_golden.py @@ -0,0 +1,273 @@ +"""Canonical hashes for the compute/HTTP class split. + +Fictional smoke chart only. Clock fields are removed before hashing. +Payload values are not written; the golden file stores sha256 and key-path types. +""" +from __future__ import annotations + +import hashlib +import json +import os +import shutil +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[2] +SCRIPTS = ROOT / "scripts" +GOLDEN_PATH = ROOT / "tests" / "golden" / "api_server_service_extract_v1.json" +CACHE_DIR = ROOT / "scratch" / "local" / "api_chart_cache" +CLOCK_KEYS = {"cache_created_at", "cache_expires_at"} +THEMES = ("career", "wealth", "relationship") +BASELINE_SHA = "fbf04f0b5f31cd8421bc4546840b1c833aaed810" + +# Synthetic positions reused from tests/test_offline_compute_mixins.py. +# Not a birth record. +EVIDENCE_CHART = { + "success": True, + "ascendant": {"sign": "Aries", "sign_idx": 0, "degree": 12.0, "lon": 12.0}, + "planets": { + "Sun": {"sign": "Leo", "lon": 130.0}, + "Moon": {"sign": "Cancer", "lon": 100.0}, + }, +} + + +def prepare_env() -> None: + os.environ["VEDASTRO_ENABLE_NETWORK"] = "0" + os.environ.setdefault("JYOTISH_SKIP_LOCAL_ENV", "1") + os.environ["PYTHONDONTWRITEBYTECODE"] = "1" + for path in (str(ROOT), str(SCRIPTS)): + if path not in sys.path: + sys.path.insert(0, path) + + +def clear_chart_cache() -> None: + if CACHE_DIR.exists(): + shutil.rmtree(CACHE_DIR) + + +def theme_body(theme: str) -> dict[str, Any]: + """Fictional smoke chart from the task brief. Coordinates only, no person.""" + return { + "year": 1990, + "month": 1, + "day": 1, + "hour": 12, + "minute": 0, + "second": 0, + "lat": 28.6139, + "lon": 77.2090, + "tz": 5.5, + "theme": [theme], + "defer_optional_external_evidence": True, + "today": "2026-10-13", + } + + +def strip_clock(value: Any) -> Any: + if isinstance(value, dict): + return { + key: strip_clock(item) + for key, item in value.items() + if key not in CLOCK_KEYS + } + if isinstance(value, list): + return [strip_clock(item) for item in value] + return value + + +def json_type(value: Any) -> str: + if value is None: + return "null" + if isinstance(value, bool): + return "boolean" + if isinstance(value, (int, float)): + return "number" + if isinstance(value, str): + return "string" + if isinstance(value, list): + return "array" + if isinstance(value, dict): + return "object" + return type(value).__name__ + + +def flatten_keypaths(value: Any, prefix: str = "", *, depth: int = 0, max_depth: int = 2) -> dict[str, str]: + out: dict[str, str] = {} + label = prefix or "$" + out[label] = json_type(value) + if depth >= max_depth: + return out + if isinstance(value, dict): + for key in sorted(item for item in value if isinstance(item, str)): + child = f"{prefix}.{key}" if prefix else key + out.update(flatten_keypaths(value[key], child, depth=depth + 1, max_depth=max_depth)) + elif isinstance(value, list) and value: + child = f"{prefix}[]" if prefix else "[]" + out.update(flatten_keypaths(value[0], child, depth=depth + 1, max_depth=max_depth)) + return out + + +def canonical_text(value: Any) -> str: + return json.dumps(strip_clock(value), sort_keys=True, ensure_ascii=False) + + +def digest(value: Any) -> dict[str, Any]: + text = canonical_text(value) + return { + "sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(), + "canonicalBytes": len(text.encode("utf-8")), + "keypaths": flatten_keypaths(strip_clock(value)), + } + + +def capture_mcp(theme: str) -> Any: + prepare_env() + clear_chart_cache() + from consultation_workflow_service import execute_consultation_workflow + + return execute_consultation_workflow(theme_body(theme), surface="skill_mcp") + + +def capture_http(theme: str) -> Any: + """HTTP handler method, on the compute class after the split.""" + prepare_env() + clear_chart_cache() + from jyotish_api_server import JyotishComputeService + + return JyotishComputeService()._compute_consultation_workflow(theme_body(theme)) + + +def capture_repairs() -> dict[str, Any]: + prepare_env() + clear_chart_cache() + from capture_report_blocked_repairs_golden import ( + BIRTH, + MODULE_KEEP, + THEMES as REPAIR_THEMES, + VARGA_KEEP, + _body, + _handler, + _trim_workflow, + ) + + payload: dict[str, Any] = { + "source": "scripts/jyotish_api_server.py _compute_consultation_workflow", + "smoke_birth": {**BIRTH, "note": "fictional capture identity; not a real person"}, + "trim": { + "keep_varga_keys": list(VARGA_KEEP), + "keep_modules": list(MODULE_KEEP), + "drop": [ + "ai_prompt_pack", + "vedastro", + "varga_spectrum", + "guided_topics", + "western_spectrum", + "technique_audit_table", + ], + }, + "themes": {}, + } + for theme in REPAIR_THEMES: + clear_chart_cache() + raw = _handler()._compute_consultation_workflow(_body(theme)) + payload["themes"][theme] = _trim_workflow(raw) + return payload + + +def capture_evidence() -> Any: + prepare_env() + from consultation_workflow_service import build_runtime_evidence_helpers + + return build_runtime_evidence_helpers(EVIDENCE_CHART) + + +def capture_cases(*, include_http: bool = True, include_repairs: bool = True) -> dict[str, Any]: + cases: dict[str, Any] = {} + for theme in THEMES: + cases[f"mcp.{theme}"] = capture_mcp(theme) + if include_http: + cases[f"http.{theme}"] = capture_http(theme) + if include_repairs: + cases["repairs"] = capture_repairs() + cases["evidence"] = capture_evidence() + return cases + + +def summarize(cases: dict[str, Any]) -> dict[str, Any]: + return {name: digest(value) for name, value in cases.items()} + + +def provenance() -> dict[str, Any]: + return { + "fictional": True, + "command": "python scripts/research/api_service_extract_golden.py --check", + "baselineSha": BASELINE_SHA, + "env": { + "VEDASTRO_ENABLE_NETWORK": "0", + "JYOTISH_SKIP_LOCAL_ENV": "1", + }, + "clockFieldsRemoved": sorted(CLOCK_KEYS), + "mcpSurface": "skill_mcp", + "note": "fictional smoke chart from the task brief; payload values are not stored", + } + + +def document_from_summary(summary: dict[str, Any]) -> dict[str, Any]: + return { + "kind": "sha256_and_keypath_types", + "fixtureProvenance": provenance(), + "cases": summary, + } + + +def write_golden(summary: dict[str, Any], path: Path = GOLDEN_PATH) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + text = json.dumps(document_from_summary(summary), ensure_ascii=False, indent=2, sort_keys=True) + "\n" + path.write_text(text, encoding="utf-8") + + +def load_golden(path: Path = GOLDEN_PATH) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def check_golden(path: Path = GOLDEN_PATH) -> list[str]: + expected = load_golden(path)["cases"] + actual = summarize(capture_cases()) + mismatches: list[str] = [] + if set(expected) != set(actual): + mismatches.append(f"case names {sorted(expected)} != {sorted(actual)}") + for name in sorted(set(expected) & set(actual)): + if expected[name]["sha256"] != actual[name]["sha256"]: + mismatches.append( + f"{name} sha256 {expected[name]['sha256']} != {actual[name]['sha256']} " + f"bytes {expected[name]['canonicalBytes']} != {actual[name]['canonicalBytes']}" + ) + elif expected[name]["keypaths"] != actual[name]["keypaths"]: + mismatches.append(f"{name} keypaths differ") + return mismatches + + +def main(argv: list[str]) -> int: + prepare_env() + if "--check" in argv: + mismatches = check_golden() + if mismatches: + print("MISMATCH") + for item in mismatches: + print(item) + return 1 + print("MATCH", len(load_golden()["cases"])) + return 0 + summary = summarize(capture_cases()) + for name, row in summary.items(): + print(f"{name} {row['sha256']} {row['canonicalBytes']}") + if "--write" in argv: + write_golden(summary) + print("WROTE", GOLDEN_PATH) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/tests/golden/api_server_service_extract_v1.json b/tests/golden/api_server_service_extract_v1.json new file mode 100644 index 00000000..341fa74a --- /dev/null +++ b/tests/golden/api_server_service_extract_v1.json @@ -0,0 +1,1966 @@ +{ + "cases": { + "evidence": { + "canonicalBytes": 2092, + "keypaths": { + "$": "object", + "interpretation_coverage": "object", + "interpretation_coverage.boundary": "string", + "interpretation_coverage.not_fully_closed": "array", + "interpretation_coverage.proven_runtime_markers": "array", + "interpretation_coverage.runtime_visibility_status": "string", + "interpretation_coverage.source_pack_status": "string", + "vedastro_archive_manifest": "object", + "vedastro_archive_manifest.archive_count": "number", + "vedastro_archive_manifest.archives": "array", + "vedastro_archive_manifest.scope": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null" + }, + "sha256": "27975ba40a9ee89fc19543a18cb2ee60fb2477b8a6e8f14004ad411adfc408a2" + }, + "http.career": { + "canonicalBytes": 420555, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string" + }, + "sha256": "6a6ca421d942e2577e587647324ef9521ea14841a74a6850c11f629df4d9ba96" + }, + "http.relationship": { + "canonicalBytes": 414472, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string" + }, + "sha256": "4405ae821aef81ec03660994d9f8f4104ae3388f339407bfe2721d05d662059b" + }, + "http.wealth": { + "canonicalBytes": 419316, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string" + }, + "sha256": "f22368bbde38b76dc44e2995c34f7809ca96442405c1f13e39f53c4a2dd90a62" + }, + "mcp.career": { + "canonicalBytes": 543868, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string", + "western_evidence_packet": "object", + "western_evidence_packet.boundary": "string", + "western_evidence_packet.calculation": "object", + "western_evidence_packet.license_boundary": "string", + "western_evidence_packet.missing_sections": "array", + "western_evidence_packet.natal": "object", + "western_evidence_packet.native_chart": "object", + "western_evidence_packet.required_sections": "array", + "western_evidence_packet.route": "object", + "western_evidence_packet.sections": "object", + "western_evidence_packet.signals": "array", + "western_evidence_packet.source_engine": "string", + "western_evidence_packet.status": "string", + "western_evidence_packet.system": "string", + "western_evidence_packet.timing_techniques": "object" + }, + "sha256": "ad38a539cf9036143333acd538567b7eb94528d537c46ef326905ebc47527a05" + }, + "mcp.relationship": { + "canonicalBytes": 537785, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string", + "western_evidence_packet": "object", + "western_evidence_packet.boundary": "string", + "western_evidence_packet.calculation": "object", + "western_evidence_packet.license_boundary": "string", + "western_evidence_packet.missing_sections": "array", + "western_evidence_packet.natal": "object", + "western_evidence_packet.native_chart": "object", + "western_evidence_packet.required_sections": "array", + "western_evidence_packet.route": "object", + "western_evidence_packet.sections": "object", + "western_evidence_packet.signals": "array", + "western_evidence_packet.source_engine": "string", + "western_evidence_packet.status": "string", + "western_evidence_packet.system": "string", + "western_evidence_packet.timing_techniques": "object" + }, + "sha256": "be70fd603b1de52026ad40f0d6582a27cb8b148018474fcd7d8c4134509b40ae" + }, + "mcp.wealth": { + "canonicalBytes": 542629, + "keypaths": { + "$": "object", + "answer_policy": "object", + "answer_policy.blocked_techniques": "array", + "answer_policy.deterministic_claims_forbidden_for": "array", + "answer_policy.technique_truth_status": "string", + "audited_remedies": "object", + "audited_remedies.reason": "string", + "audited_remedies.source": "string", + "audited_remedies.status": "string", + "birth_time_sensitivity": "object", + "birth_time_sensitivity.accuracy": "string", + "birth_time_sensitivity.schema": "string", + "birth_time_sensitivity.status": "string", + "boundary": "string", + "chart": "object", + "chart.ai_prompt_pack": "object", + "chart.arudha_padas": "object", + "chart.ascendant": "object", + "chart.available_dashas": "array", + "chart.birth": "object", + "chart.calculation_contract": "object", + "chart.consultation_native_layers": "object", + "chart.dasha": "object", + "chart.dasha_count": "number", + "chart.houses": "object", + "chart.local_consultation_layers": "object", + "chart.modules": "object", + "chart.planets": "object", + "chart.remedies": "object", + "chart.result_hash": "string", + "chart.runtime_cache": "object", + "chart.sade_sati": "object", + "chart.shadbala": "object", + "chart.special_lagnas": "object", + "chart.success": "boolean", + "chart.tithi_lord_analysis": "object", + "chart.version": "string", + "chart.yogas": "array", + "commercial_evidence_status": "object", + "commercial_evidence_status.claim_audit": "object", + "commercial_evidence_status.three_engine_mismatch": "object", + "commercial_evidence_status.vedastro_identity": "object", + "consultation_plan_contract": "null", + "consumer_context": "object", + "consumer_context.answer_policy": "object", + "consumer_context.available_layers": "array", + "consumer_context.calculation_source": "string", + "consumer_context.commercial_evidence_status": "object", + "consumer_context.core_status": "string", + "consumer_context.domain_boundaries": "object", + "consumer_context.domain_context_layers": "array", + "consumer_context.hard_blockers": "array", + "consumer_context.missing_route_layers": "array", + "consumer_context.optional_unavailable_layers": "array", + "consumer_context.precision": "object", + "consumer_context.provider_status": "object", + "consumer_context.route": "string", + "consumer_context.technique_audit_table": "array", + "consumer_context.technique_truth": "object", + "consumer_context.user_facing_limitation": "null", + "consumer_context.varga_spectrum": "object", + "consumer_context.vedastro_cross_check": "object", + "consumer_context.western_spectrum": "object", + "endpoint": "string", + "entry_mode": "string", + "external_parity_gate": "object", + "external_parity_gate.birth_data_policy": "string", + "external_parity_gate.blocked_reason": "null", + "external_parity_gate.blocked_row_count": "number", + "external_parity_gate.case_id": "string", + "external_parity_gate.comparison_row_count": "number", + "external_parity_gate.covered_sections": "array", + "external_parity_gate.engine_count": "number", + "external_parity_gate.errors": "array", + "external_parity_gate.manifest_path": "string", + "external_parity_gate.match_count": "number", + "external_parity_gate.mismatch_count": "number", + "external_parity_gate.missing_engines": "array", + "external_parity_gate.missing_high_rigor_sections": "array", + "external_parity_gate.not_comparable_count": "number", + "external_parity_gate.required_engines": "array", + "external_parity_gate.runtime_boundary": "string", + "external_parity_gate.status": "string", + "external_parity_gate.tested": "boolean", + "historical_event_backtest": "object", + "interpretation_source_runtime_coverage": "object", + "interpretation_source_runtime_coverage.boundary": "string", + "interpretation_source_runtime_coverage.not_fully_closed": "array", + "interpretation_source_runtime_coverage.proven_runtime_markers": "array", + "interpretation_source_runtime_coverage.runtime_visibility_status": "string", + "interpretation_source_runtime_coverage.source_pack_status": "string", + "machine_evidence_packet": "object", + "machine_evidence_packet.functional_benefic_malefic": "object", + "machine_evidence_packet.missing_sections": "array", + "machine_evidence_packet.required_sections": "array", + "machine_evidence_packet.route": "object", + "machine_evidence_packet.sections": "object", + "machine_evidence_packet.signals": "array", + "machine_evidence_packet.status": "string", + "mode": "string", + "module_execution_audit": "array", + "module_execution_audit[]": "object", + "muhurta_panchanga": "object", + "next_questions": "array", + "next_questions[]": "object", + "prashna": "object", + "pratyantar_dasha_timeline": "object", + "pratyantar_dasha_timeline.boundary": "string", + "pratyantar_dasha_timeline.current": "object", + "pratyantar_dasha_timeline.next": "object", + "pratyantar_dasha_timeline.status": "string", + "question": "string", + "real_case_calibration": "object", + "real_case_calibration.annual_control_pilot": "object", + "real_case_calibration.batch_id": "string", + "real_case_calibration.boundary": "string", + "real_case_calibration.candidate_refs": "array", + "real_case_calibration.case_index_by_domain": "object", + "real_case_calibration.corrected_v21_observation": "object", + "real_case_calibration.holdout_replay_manifest": "object", + "real_case_calibration.negative_control_pilot": "object", + "real_case_calibration.outcome_replay_manifest": "object", + "real_case_calibration.public_outcome_benchmark": "object", + "real_case_calibration.reference_grade": "string", + "real_case_calibration.required_replay_schema": "string", + "real_case_calibration.route": "string", + "real_case_calibration.scored_candidates": "array", + "real_case_calibration.source_roots": "array", + "real_case_calibration.status": "string", + "real_case_calibration.supplemental_public_probe": "object", + "real_case_calibration.timing_precision_gate": "object", + "rectification": "object", + "rectification.ascendant": "object", + "rectification.declared_accuracy": "string", + "rectification.effective_accuracy": "string", + "rectification.enabled_vargas": "object", + "rectification.endpoint": "string", + "rectification.lagna_boundary": "object", + "rectification.success": "boolean", + "rectification.summary": "object", + "rectification.time_source": "string", + "rectification.vedastro_gateway": "object", + "reference_transparency": "object", + "reference_transparency.external_engine_observations": "object", + "reference_transparency.method_variants": "object", + "reference_transparency.similar_public_cases": "object", + "reference_transparency.timing_display": "object", + "reference_transparency.version": "string", + "reused_modules": "array", + "reused_modules[]": "string", + "routes": "array", + "routes[]": "string", + "routing": "object", + "routing.audit_route": "string", + "routing.display_label": "string", + "routing.document_route": "string", + "routing.focus_techniques": "array", + "routing.primary_theme": "string", + "routing.question_type": "string", + "routing.registry_route": "string", + "routing.route_aliases": "array", + "routing.route_source": "string", + "routing.runtime_route": "string", + "runtime_evidence_log": "object", + "runtime_evidence_log.blind_technical_mode": "object", + "runtime_evidence_log.cross_system_arbitration": "object", + "runtime_evidence_log.entry_mode": "string", + "runtime_evidence_log.evidence_packet_contract": "object", + "runtime_evidence_log.evidence_sources": "object", + "runtime_evidence_log.executed_steps": "array", + "runtime_evidence_log.external_engine_cross_validation": "object", + "runtime_evidence_log.finance_astrology_support_review": "object", + "runtime_evidence_log.kp_western_support": "object", + "runtime_evidence_log.name": "string", + "runtime_evidence_log.quality_gate": "object", + "runtime_evidence_log.real_case_calibration": "object", + "runtime_evidence_log.route": "object", + "runtime_evidence_log.skipped_steps": "array", + "runtime_evidence_log.source_priority": "object", + "runtime_evidence_log.specialized_indian_closure_review": "object", + "runtime_evidence_log.surface": "string", + "runtime_evidence_log.vedastro_cloud_state": "string", + "runtime_evidence_log.vedastro_runtime_truth": "object", + "runtime_planner": "object", + "runtime_planner.async_candidates": "array", + "runtime_planner.boundary": "string", + "runtime_planner.entry_mode": "string", + "runtime_planner.executed_steps": "array", + "runtime_planner.high_rigor": "boolean", + "runtime_planner.planner_name": "string", + "runtime_planner.question_context": "object", + "runtime_planner.reuse_contract": "object", + "runtime_planner.route": "object", + "runtime_planner.skipped_steps": "array", + "runtime_planner.source_priority": "object", + "runtime_planner.surface": "string", + "runtime_planner.sync_steps": "array", + "runtime_truth": "object", + "runtime_truth.blocked_items": "array", + "runtime_truth.catalog_boundary": "string", + "runtime_truth.conflicts": "array", + "runtime_truth.fallback_active": "boolean", + "runtime_truth.free_tier_strategy": "object", + "runtime_truth.official_execution_layers": "object", + "runtime_truth.primary_route": "null", + "runtime_truth.routes_available": "array", + "runtime_truth.status": "string", + "source_priority": "object", + "source_priority.boundary": "string", + "source_priority.mode": "string", + "source_priority.priority": "array", + "success": "boolean", + "technique_truth": "object", + "technique_truth.blocked_techniques": "array", + "technique_truth.partial_techniques": "array", + "technique_truth.reference_only_techniques": "array", + "technique_truth.status": "string", + "technique_truth.techniques": "array", + "thematic_report": "object", + "thematic_report.available_themes": "array", + "thematic_report.boundary": "string", + "thematic_report.consultation_thematic_capabilities": "object", + "thematic_report.endpoint": "string", + "thematic_report.evidence_source": "object", + "thematic_report.fragment_sources": "array", + "thematic_report.independent_report_themes": "array", + "thematic_report.mode": "string", + "thematic_report.native_report_themes": "array", + "thematic_report.source": "string", + "thematic_report.success": "boolean", + "thematic_report.theme_count": "number", + "thematic_report.themes": "object", + "thematic_report.workflow_orchestration": "object", + "themes": "array", + "themes[]": "string", + "timing_narrative": "object", + "timing_narrative.boundaries": "array", + "timing_narrative.headline": "string", + "timing_narrative.risks": "array", + "timing_narrative.status": "string", + "timing_narrative.strengths": "array", + "timing_precision_contract": "object", + "timing_precision_contract.boundary": "string", + "timing_precision_contract.candidate_windows": "array", + "timing_precision_contract.claim_status": "string", + "timing_precision_contract.exact_triggers": "array", + "timing_precision_contract.promotion_gate": "object", + "timing_precision_contract.tiers": "object", + "timing_precision_contract.timing_precision": "string", + "timing_precision_contract.verified_window": "null", + "unified_orchestrator": "object", + "unified_orchestrator.entry_mode": "string", + "unified_orchestrator.name": "string", + "unified_orchestrator.question": "string", + "unified_orchestrator.route": "object", + "unified_orchestrator.route_profile_contract": "object", + "unified_orchestrator.shared_capabilities": "array", + "unified_orchestrator.source_priority": "object", + "unified_orchestrator.surface": "string", + "unified_orchestrator.themes": "array", + "vedastro_gateway": "object", + "vedastro_gateway.official_closure_reason": "string", + "vedastro_gateway.official_closure_state": "string", + "vedastro_gateway.scope": "string", + "vedastro_gateway.status": "string", + "vedastro_official": "object", + "vedastro_official.adjudication_stages": "object", + "vedastro_official.blocked_items": "array", + "vedastro_official.boundary": "string", + "vedastro_official.conflicts": "array", + "vedastro_official.dominant_label": "null", + "vedastro_official.event_count": "number", + "vedastro_official.fallback_used": "array", + "vedastro_official.local_supplemental_evidence": "object", + "vedastro_official.main_conflicts": "array", + "vedastro_official.multi_reference_reading_summary": "object", + "vedastro_official.official_closure_reason": "string", + "vedastro_official.official_closure_state": "string", + "vedastro_official.official_full_capability_catalog_status": "null", + "vedastro_official.official_full_capability_catalog_summary": "object", + "vedastro_official.official_full_capability_domain_routing": "object", + "vedastro_official.official_full_capability_dynamic_selection": "object", + "vedastro_official.official_primary_evidence": "object", + "vedastro_official.official_report_references": "object", + "vedastro_official.range_scan_status": "null", + "vedastro_official.raw_response": "null", + "vedastro_official.runtime_truth": "object", + "vedastro_official.status": "string", + "vedastro_official.strict_workflow_contracts": "object", + "vedastro_official.strict_workflow_primary_route": "null", + "vedastro_official.strict_workflow_routes_available": "array", + "vedastro_official.technique_audit_summary": "object", + "vedastro_official.verdict": "null", + "vedastro_snapshot_cache": "object", + "vedastro_snapshot_cache.freshness": "string", + "vedastro_snapshot_cache.reference_date": "string", + "vedastro_snapshot_cache.refresh_submitted": "boolean", + "vedastro_snapshot_cache.require_today": "boolean", + "vedastro_snapshot_cache.served_on_utc_date": "string", + "western_evidence_packet": "object", + "western_evidence_packet.boundary": "string", + "western_evidence_packet.calculation": "object", + "western_evidence_packet.license_boundary": "string", + "western_evidence_packet.missing_sections": "array", + "western_evidence_packet.natal": "object", + "western_evidence_packet.native_chart": "object", + "western_evidence_packet.required_sections": "array", + "western_evidence_packet.route": "object", + "western_evidence_packet.sections": "object", + "western_evidence_packet.signals": "array", + "western_evidence_packet.source_engine": "string", + "western_evidence_packet.status": "string", + "western_evidence_packet.system": "string", + "western_evidence_packet.timing_techniques": "object" + }, + "sha256": "9aaacf64d08f17bca2d3f6b020b1dbb1ec07eaee7727b04bba90430fcbf594d3" + }, + "repairs": { + "canonicalBytes": 74700, + "keypaths": { + "$": "object", + "smoke_birth": "object", + "smoke_birth.city": "string", + "smoke_birth.day": "number", + "smoke_birth.hour": "number", + "smoke_birth.lat": "number", + "smoke_birth.lon": "number", + "smoke_birth.minute": "number", + "smoke_birth.month": "number", + "smoke_birth.note": "string", + "smoke_birth.tz": "number", + "smoke_birth.year": "number", + "source": "string", + "themes": "object", + "themes.career": "object", + "themes.marriage": "object", + "themes.timing": "object", + "themes.wealth": "object", + "trim": "object", + "trim.drop": "array", + "trim.keep_modules": "array", + "trim.keep_varga_keys": "array" + }, + "sha256": "41d48a453fbd5d616de083fe07bb8d318c3f60ad2e127764237ef6ae50cc6175" + } + }, + "fixtureProvenance": { + "baselineSha": "fbf04f0b5f31cd8421bc4546840b1c833aaed810", + "clockFieldsRemoved": [ + "cache_created_at", + "cache_expires_at" + ], + "command": "python scripts/research/api_service_extract_golden.py --check", + "env": { + "JYOTISH_SKIP_LOCAL_ENV": "1", + "VEDASTRO_ENABLE_NETWORK": "0" + }, + "fictional": true, + "mcpSurface": "skill_mcp", + "note": "fictional smoke chart from the task brief; payload values are not stored" + }, + "kind": "sha256_and_keypath_types" +} diff --git a/tests/test_active_rectification_api.py b/tests/test_active_rectification_api.py index 07e7b187..18541c20 100644 --- a/tests/test_active_rectification_api.py +++ b/tests/test_active_rectification_api.py @@ -14,11 +14,11 @@ if str(SCRIPTS) not in sys.path: sys.path.insert(0, str(SCRIPTS)) import jyotish_api_server as api_server # noqa: E402 -from jyotish_api_server import BadRequest, JyotishAPIHandler # noqa: E402 +from jyotish_api_server import BadRequest, JyotishComputeService # noqa: E402 -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() def test_active_rectification_questions_api_builds_choice_workflow() -> None: @@ -515,7 +515,7 @@ def test_high_rigor_event_rectification_requires_real_vedastro_candidate_discrim }, ) monkeypatch.setattr( - JyotishAPIHandler, + JyotishComputeService, "_compute_vedastro_gateway_run", lambda *_args, **_kwargs: { "status": "ok", @@ -633,7 +633,7 @@ def test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow(mon }, ) monkeypatch.setattr( - JyotishAPIHandler, + JyotishComputeService, "_compute_vedastro_gateway_run", lambda *_args, **_kwargs: { "status": "ok", @@ -825,7 +825,7 @@ def test_search_events_difference_cannot_override_identical_minute_snapshots(mon }, ) monkeypatch.setattr( - JyotishAPIHandler, + JyotishComputeService, "_compute_vedastro_gateway_run", lambda *_args, **_kwargs: { "status": "ok", diff --git a/tests/test_active_rectification_events_v4.py b/tests/test_active_rectification_events_v4.py index 2474c005..41400df7 100644 --- a/tests/test_active_rectification_events_v4.py +++ b/tests/test_active_rectification_events_v4.py @@ -77,14 +77,14 @@ def test_v4_wrapper_uses_raman_spec_and_never_confirms_a_minute(monkeypatch) -> def test_v4_api_validates_event_kind_and_date_boundaries(monkeypatch) -> None: - from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler + from scripts.jyotish_api_server import BadRequest, JyotishComputeService captured = {} monkeypatch.setattr( "scripts.rectification.api_service.score_candidates", lambda request: captured.setdefault("request", request) or {}, ) - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() body = { "birth_date": "1997-08-08", "start_time": "05:00", diff --git a/tests/test_api_server_growth_contract.py b/tests/test_api_server_growth_contract.py index 4350b81a..3dda595d 100644 --- a/tests/test_api_server_growth_contract.py +++ b/tests/test_api_server_growth_contract.py @@ -2,11 +2,12 @@ New endpoints and features must live in new modules and be thinly registered from the main file. Line count is only a coarse guardrail; the live gates are -JyotishAPIHandler method count and JyotishAPIHandler.__new__ forgery sites. +handler method count and JyotishAPIHandler.__new__ forgery sites. -Baselines re-measured 2026-09-16 after TASK-api-server-backdoor-close-20260916 -moved seven compute-only methods into scripts/offline_compute_mixins.py and -closed two of the four production forgeries. +Baselines re-measured 2026-10-16 after TASK-api-server-service-extract-20261013 +moved HTTP methods onto JyotishAPIHandler and left calculation on +JyotishComputeService. Production forgeries went from 2 to 0. The two classes +stay in this file so existing module-attribute patches still land. """ from __future__ import annotations @@ -23,6 +24,9 @@ AGENTS = ROOT / "AGENTS.md" # Live `wc -l scripts/jyotish_api_server.py` equivalent: # Path.read_bytes().count(b"\n"), measured 2026-09-16 on origin/staging @ 51a65d92. # New features must not consume this budget; open a module instead. +# 原值:10924 +# 新值:10924 +# 原因:行数基线按任务书保持不动。拆类只加了类声明,实测行数仍不超过这条基线。 JYOTISH_API_SERVER_LINE_COUNT_BASELINE = 10924 JYOTISH_API_SERVER_LINE_COUNT_CAP = JYOTISH_API_SERVER_LINE_COUNT_BASELINE + 300 @@ -30,22 +34,21 @@ JYOTISH_API_SERVER_LINE_COUNT_CAP = JYOTISH_API_SERVER_LINE_COUNT_BASELINE + 300 # TASK-freeze-metric-change-20260915 §1. Re-measured 2026-09-16 after # TASK-api-server-backdoor-close-20260916 lifted 7 compute-only methods into # scripts/offline_compute_mixins.py (225 -> 218). -# 原值:218(删占位回退之前) +# 原值:217 # 新值:217 -# 原因:BUG-1341 删除 JyotishAPIHandler._fallback_dasha_periods,类方法少 1 个,没有新增方法 +# 原因:BUG-1341 已把基线降到 217。本单只把 HTTP 方法换到子类,不增删方法。 JYOTISH_API_HANDLER_METHOD_COUNT_BASELINE = 217 -# `JyotishAPIHandler.__new__` in scripts/ and tests/ `*.py`, excluding this file. -# Re-measured 2026-09-16 after TASK-api-server-backdoor-close-20260916: -# scripts/ production forgeries 4 -> 2, tests/ unchanged at 29. -JYOTISH_API_HANDLER_NEW_COUNT_BASELINE = 31 +# 原值:31 +# 新值:5 +# 原因:生产 2 处和只用计算方法的测试改为 JyotishComputeService()。留下的 5 处要碰 HTTP,本单不改。 +JYOTISH_API_HANDLER_NEW_COUNT_BASELINE = 5 -# Production forgeries only. The two survivors both sit on the consultation -# workflow chain (`consultation_workflow_service.execute_consultation_workflow` -# and `capture_report_blocked_repairs_golden`); closing them needs the 4,086-line -# mixin extraction that the 2026-09-16 spike showed breaks cross-module -# monkeypatching in tests/test_api_server_security.py. Target stays 0. -JYOTISH_API_HANDLER_NEW_SCRIPTS_BASELINE = 2 +# Production forgeries only. Offline callers instantiate JyotishComputeService. +# 原值:2 +# 新值:0 +# 原因:consultation_workflow_service 与 capture_report_blocked_repairs_golden 改为正常实例化计算类。 +JYOTISH_API_HANDLER_NEW_SCRIPTS_BASELINE = 0 # Compute-only mixins lifted out of the handler must never depend back on the # HTTP monolith, or the backdoor simply moves house. @@ -141,3 +144,88 @@ def test_quality_gate_runs_api_server_growth_contract() -> None: assert "tests/test_api_server_growth_contract.py" in CORE_PYTEST_TARGETS quality_gate = (ROOT / "scripts" / "run_quality_gate.py").read_text(encoding="utf-8") assert '"tests/test_api_server_growth_contract.py"' in quality_gate + + +_HTTP_ATTRS = frozenset({ + "headers", + "wfile", + "rfile", + "client_address", + "send_response", + "send_header", + "end_headers", + "path", + "command", +}) +_HTTP_METHOD_NAMES = frozenset({ + "_json", + "_html", + "_send_cors_headers", + "_read_json_body", + "_enforce_request_security", + "_job_access_token", + "do_GET", + "do_POST", + "do_OPTIONS", + "_error_json", + "_get_chart_job", + "_get_high_rigor_job", + "_require_dynamic_rectification_token", + "_compute_dynamic_rectification_score", + "_compute_dynamic_rectification_opportunities", +}) + + +def test_compute_service_is_not_an_http_handler() -> None: + """JyotishComputeService can be constructed. HTTP stays on the subclass.""" + from http.server import BaseHTTPRequestHandler + + from scripts.jyotish_api_server import JyotishAPIHandler, JyotishComputeService + + assert not issubclass(JyotishComputeService, BaseHTTPRequestHandler) + assert issubclass(JyotishAPIHandler, JyotishComputeService) + assert issubclass(JyotishAPIHandler, BaseHTTPRequestHandler) + mro_names = [item.__name__ for item in JyotishAPIHandler.__mro__] + assert mro_names.index("BaseHTTPRequestHandler") < mro_names.index("JyotishComputeService") + assert JyotishAPIHandler.server_version == "JyotishAPI/6.9.14" + assert "server_version" not in JyotishComputeService.__dict__ + for name in _HTTP_METHOD_NAMES: + assert name in JyotishAPIHandler.__dict__, name + assert name not in JyotishComputeService.__dict__, name + + +def _http_touches(fn: ast.AST) -> list[str]: + hits: list[str] = [] + for node in ast.walk(fn): + if ( + isinstance(node, ast.Attribute) + and isinstance(node.value, ast.Name) + and node.value.id == "self" + and node.attr in _HTTP_ATTRS | _HTTP_METHOD_NAMES + ): + hits.append(f"{node.lineno}:{node.attr}") + if ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "getattr" + and len(node.args) >= 2 + and isinstance(node.args[0], ast.Name) + and node.args[0].id == "self" + and isinstance(node.args[1], ast.Constant) + and node.args[1].value in _HTTP_ATTRS | _HTTP_METHOD_NAMES + ): + hits.append(f"{node.lineno}:getattr:{node.args[1].value}") + return hits + + +def test_compute_service_methods_do_not_touch_http() -> None: + tree = ast.parse(API_SERVER.read_text(encoding="utf-8")) + compute = next( + node for node in tree.body + if isinstance(node, ast.ClassDef) and node.name == "JyotishComputeService" + ) + offenders: list[str] = [] + for node in compute.body: + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + offenders.extend(f"{node.name}:{hit}" for hit in _http_touches(node)) + assert offenders == [] diff --git a/tests/test_api_service_extract_golden.py b/tests/test_api_service_extract_golden.py new file mode 100644 index 00000000..9d8de38a --- /dev/null +++ b/tests/test_api_service_extract_golden.py @@ -0,0 +1,17 @@ +"""Recompute the fictional smoke chart and compare canonical hashes. + +The stored golden keeps sha256 and key-path types only. +""" +from __future__ import annotations + +import importlib.util +from pathlib import Path + + +def test_api_service_extract_golden_matches_baseline() -> None: + path = Path(__file__).resolve().parents[1] / "scripts" / "research" / "api_service_extract_golden.py" + spec = importlib.util.spec_from_file_location("api_service_extract_golden", path) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + assert module.check_golden() == [] diff --git a/tests/test_calculation_p0_regressions.py b/tests/test_calculation_p0_regressions.py index 4686b5fe..f3638b2d 100644 --- a/tests/test_calculation_p0_regressions.py +++ b/tests/test_calculation_p0_regressions.py @@ -13,7 +13,7 @@ if str(SCRIPTS) not in sys.path: import domain_calculation_service as calculation_service # noqa: E402 import jyotish_api_server # noqa: E402 -from jyotish_api_server import JyotishAPIHandler # noqa: E402 +from jyotish_api_server import JyotishComputeService # noqa: E402 from jyotish_engine import _compute_chart_from_args # noqa: E402 BIRTH = { @@ -121,7 +121,7 @@ def test_chart_hash_matches_domain_cli_and_rest( cli, _asc_idx, _jd, _ayanamsa = _compute_chart_from_args( SimpleNamespace(**BIRTH, node_mode="true") ) - rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync( + rest = JyotishComputeService()._compute_chart_sync( {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"} ) diff --git a/tests/test_commercial_domain_calculation_contract.py b/tests/test_commercial_domain_calculation_contract.py index cd1f8c5d..aacdb1da 100644 --- a/tests/test_commercial_domain_calculation_contract.py +++ b/tests/test_commercial_domain_calculation_contract.py @@ -11,7 +11,7 @@ if str(SCRIPTS) not in sys.path: import domain_calculation_service as calculation_service # noqa: E402 import jyotish_api_server # noqa: E402 -from jyotish_api_server import JyotishAPIHandler # noqa: E402 +from jyotish_api_server import JyotishComputeService # noqa: E402 BIRTH = { "year": 1990, @@ -100,7 +100,7 @@ def test_api_chart_uses_same_domain_contract_and_preserves_shape( ) -> None: import domain_calculation_service import jyotish_api_server - from jyotish_api_server import JyotishAPIHandler + from jyotish_api_server import JyotishComputeService monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0") monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0") @@ -111,7 +111,7 @@ def test_api_chart_uses_same_domain_contract_and_preserves_shape( ) expected = domain_calculation_service.compute_chart(BIRTH) - result = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync( + result = JyotishComputeService()._compute_chart_sync( {**BIRTH, "transit_date": "2026-07-11"} ) @@ -141,7 +141,7 @@ def test_api_chart_response_uses_domain_contract_hash(monkeypatch: pytest.Monkey ) expected = calculation_service.compute_chart({**BIRTH, "node_mode": "true"}) - rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync( + rest = JyotishComputeService()._compute_chart_sync( {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"} ) @@ -160,7 +160,7 @@ def test_api_visible_chart_values_come_from_domain_service(monkeypatch: pytest.M request = {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"} expected = calculation_service.compute_chart(request) - rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request) + rest = JyotishComputeService()._compute_chart_sync(request) assert rest["ascendant"]["lon"] == pytest.approx(expected["ascendant"]["lon"], abs=1e-8) for planet in ("Sun", "Moon", "Rahu", "Ketu"): @@ -187,7 +187,7 @@ def test_api_sade_sati_uses_domain_true_saturn_transit(monkeypatch: pytest.Monke ayanamsa=BIRTH["ayanamsa"], ) - rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request) + rest = JyotishComputeService()._compute_chart_sync(request) assert rest["sade_sati"]["transit_saturn_lon"] == pytest.approx( expected["transit_saturn_lon"], abs=1e-8 @@ -210,7 +210,7 @@ def test_api_dasha_boundary_comes_from_domain_service(monkeypatch: pytest.Monkey moon_lon=chart["planets"]["Moon"]["lon"], ) - rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request) + rest = JyotishComputeService()._compute_chart_sync(request) assert rest["dasha"]["current_md"] == expected["birth_balance"]["lord"] assert rest["dasha"]["remaining_years"] == pytest.approx( diff --git a/tests/test_consultation_birth_accuracy.py b/tests/test_consultation_birth_accuracy.py index 31e95aa7..ff45b79a 100644 --- a/tests/test_consultation_birth_accuracy.py +++ b/tests/test_consultation_birth_accuracy.py @@ -12,7 +12,7 @@ if SCRIPTS not in sys.path: sys.path.insert(0, SCRIPTS) from birth_time_rectifier import get_effective_accuracy, get_enabled_vargas # noqa: E402 -from jyotish_api_server import JyotishAPIHandler, _build_consumer_context # noqa: E402 +from jyotish_api_server import JyotishComputeService, _build_consumer_context # noqa: E402 CANDIDATE_WINDOW_COPY = re.compile( r'candidate windows|not_auto_rectified|按时间范围|建议先做事件反验', @@ -21,8 +21,8 @@ CANDIDATE_WINDOW_COPY = re.compile( PRECISION_ANNOTATION_COPY = re.compile(r'填报时间|标注精度|precision', re.I) -def _handler() -> JyotishAPIHandler: - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + handler = JyotishComputeService() handler._compute_vedastro_gateway_run = lambda body: { # type: ignore[method-assign] 'status': 'official_blocked', 'official_closure_reason': 'accuracy_contract_probe', diff --git a/tests/test_consultation_consumer_context.py b/tests/test_consultation_consumer_context.py index 9af114ef..49edf4ea 100644 --- a/tests/test_consultation_consumer_context.py +++ b/tests/test_consultation_consumer_context.py @@ -17,7 +17,7 @@ from jyotish_api_server import ( # noqa: E402 _PRECISE_BIRTH_TIME_ACCURACY, _ROUTE_DOMAIN_CONTEXT, _ROUTE_REQUIRED_LAYERS, - JyotishAPIHandler, + JyotishComputeService, _attach_local_consultation_layers, _build_consumer_context, _compact_vedastro_cross_check, @@ -26,8 +26,8 @@ from jyotish_api_server import ( # noqa: E402 from unified_consultation_orchestrator import UnifiedConsultationOrchestrator # noqa: E402 -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() _BIRTH = {'year': 1995, 'month': 8, 'day': 18, 'hour': 12, 'minute': 0} diff --git a/tests/test_consultation_plan_contract.py b/tests/test_consultation_plan_contract.py index 7a6e86dc..96f44f19 100644 --- a/tests/test_consultation_plan_contract.py +++ b/tests/test_consultation_plan_contract.py @@ -267,9 +267,9 @@ def _consultation_body(**overrides): def test_declared_route_executes_even_when_question_text_names_another_domain(): """One question, several domains: text routing can only ever agree with one of them.""" - from scripts.jyotish_api_server import JyotishAPIHandler + from scripts.jyotish_api_server import JyotishComputeService - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() question = "我的事业和财运接下来会怎么走,两者之间该怎么取舍" wealth = handler._compute_consultation_workflow(_consultation_body( @@ -287,9 +287,9 @@ def test_declared_route_executes_even_when_question_text_names_another_domain(): def test_declared_route_cannot_smuggle_a_route_off_the_server_allowlist(): - from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler + from scripts.jyotish_api_server import BadRequest, JyotishComputeService - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() with pytest.raises(BadRequest, match="unsupported consultation workflow route"): handler._compute_consultation_workflow(_consultation_body( @@ -308,9 +308,9 @@ def test_declared_route_cannot_smuggle_a_route_off_the_server_allowlist(): def test_api_dry_run_exposes_validated_plan_contract_and_rejects_tampering(): - from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler + from scripts.jyotish_api_server import BadRequest, JyotishComputeService - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() body = { **timing_body(), "dry_run": True, diff --git a/tests/test_consultation_workflow_birth_time_sensitivity.py b/tests/test_consultation_workflow_birth_time_sensitivity.py index 8d1c056b..bdeb19fe 100644 --- a/tests/test_consultation_workflow_birth_time_sensitivity.py +++ b/tests/test_consultation_workflow_birth_time_sensitivity.py @@ -16,7 +16,7 @@ if SCRIPTS not in sys.path: import flexible_birth_time_profile as profile_module # noqa: E402 from jyotish_engine import _birth_datetime_from_args # noqa: E402 from scripts.consultation_domain_registry import CANONICAL_DOMAINS # noqa: E402 -from scripts.jyotish_api_server import JyotishAPIHandler, _load_local_module # noqa: E402 +from scripts.jyotish_api_server import JyotishComputeService, _load_local_module # noqa: E402 _SMOKE_BIRTH = { @@ -31,11 +31,11 @@ _SMOKE_BIRTH = { } -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() -def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler) -> None: +def _stub_consultation_runtime(monkeypatch, handler: JyotishComputeService) -> None: chart = { "success": True, "birth_info": {"date": "1993-06-15", "time": "10:30", "tz": 8}, diff --git a/tests/test_consultation_workflow_domains.py b/tests/test_consultation_workflow_domains.py index 1e62ee26..2e7ea208 100644 --- a/tests/test_consultation_workflow_domains.py +++ b/tests/test_consultation_workflow_domains.py @@ -7,7 +7,7 @@ import pytest from scripts.consultation_domain_registry import CANONICAL_DOMAINS from scripts.consultation_plan_contract import plan_route_contract -from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler +from scripts.jyotish_api_server import BadRequest, JyotishComputeService from scripts.report_orchestrator import ( INDEPENDENT_REPORT_THEMES, ThemeName, @@ -18,11 +18,11 @@ from scripts.report_orchestrator import ( _NATIVE_THEMATIC_DOMAINS = {"career", "marriage", "wealth", "health"} -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() -def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler, *, include_strict_contracts: bool = True) -> None: +def _stub_consultation_runtime(monkeypatch, handler: JyotishComputeService, *, include_strict_contracts: bool = True) -> None: chart = { "success": True, "birth_info": {"date": "1997-08-08", "time": "05:00", "tz": 8}, diff --git a/tests/test_declared_window_chart.py b/tests/test_declared_window_chart.py index ff99d1b5..903f73be 100644 --- a/tests/test_declared_window_chart.py +++ b/tests/test_declared_window_chart.py @@ -79,9 +79,9 @@ def test_window_packet_probes_always_pass_hour_and_minute(monkeypatch: pytest.Mo def test_api_handler_rejects_hour_on_declared_window() -> None: - from jyotish_api_server import BadRequest, JyotishAPIHandler + from jyotish_api_server import BadRequest, JyotishComputeService - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() with pytest.raises(BadRequest, match="single birth minute"): handler._compute_declared_window_chart({ **BIRTH_PLACE, diff --git a/tests/test_flexible_birth_time_engine.py b/tests/test_flexible_birth_time_engine.py index cdf77720..68fd9133 100644 --- a/tests/test_flexible_birth_time_engine.py +++ b/tests/test_flexible_birth_time_engine.py @@ -192,7 +192,7 @@ def test_api_turns_invalid_birth_time_accuracy_into_bad_request(monkeypatch) -> return {"modules": {}} monkeypatch.setattr(api, "_load_local_module", lambda _name: FakeEngine) - handler = api.JyotishAPIHandler.__new__(api.JyotishAPIHandler) + handler = api.JyotishComputeService() with pytest.raises( api.BadRequest, @@ -218,7 +218,7 @@ def test_api_turns_invalid_report_window_into_bad_request(monkeypatch) -> None: raise ValueError("birth_time_window_must_not_cross_midnight") monkeypatch.setattr(api, "_load_local_module", lambda _name: FakeEngine) - handler = api.JyotishAPIHandler.__new__(api.JyotishAPIHandler) + handler = api.JyotishComputeService() with pytest.raises(api.BadRequest, match="birth_time_window_must_not_cross_midnight"): handler._compute_full_reading_for_thematic({ diff --git a/tests/test_health_narrative.py b/tests/test_health_narrative.py index 620f17de..53275074 100644 --- a/tests/test_health_narrative.py +++ b/tests/test_health_narrative.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from scripts.jyotish_api_server import JyotishAPIHandler +from scripts.jyotish_api_server import JyotishComputeService from scripts.jyotish_engine import _build_ai_prompt_pack, _build_health_narrative_payload @@ -44,7 +44,7 @@ def test_health_narrative_uses_only_health_vargas_lords_dasha_and_related_shadba def test_api_prompt_pack_keeps_compact_health_narrative(): report = _report() chart = {**report["chart"], "modules": report["modules"], "birth": {}, "dasha": {}} - prompt = JyotishAPIHandler.__new__(JyotishAPIHandler)._build_chart_prompt_pack(chart) + prompt = JyotishComputeService()._build_chart_prompt_pack(chart) seed = prompt["evidence_snapshot"]["health_narrative"] assert seed["status"] == "parameter_sensitive" assert "D6/D8/D30" in seed["headline"] diff --git a/tests/test_jaimini_rangacharya_api.py b/tests/test_jaimini_rangacharya_api.py index 1f649542..cface681 100644 --- a/tests/test_jaimini_rangacharya_api.py +++ b/tests/test_jaimini_rangacharya_api.py @@ -6,11 +6,11 @@ SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts") if SCRIPTS not in sys.path: sys.path.insert(0, SCRIPTS) -from jyotish_api_server import JyotishAPIHandler # noqa: E402 +from jyotish_api_server import JyotishComputeService # noqa: E402 -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() def _planets(): diff --git a/tests/test_prashna_entry_contract.py b/tests/test_prashna_entry_contract.py index 729a8326..338db9cb 100644 --- a/tests/test_prashna_entry_contract.py +++ b/tests/test_prashna_entry_contract.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest -from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler +from scripts.jyotish_api_server import BadRequest, JyotishComputeService ROOT = Path(__file__).resolve().parents[1] @@ -39,7 +39,7 @@ def test_cli_prashna_blocks_legacy_approximation_modes(): def test_api_prashna_rejects_client_planets_and_computes_context(): - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() body = { "question_text": "Test question", "question_timestamp": "2026-07-12T12:00:00+08:00", "lat": 39.9042, "lon": 116.4074, "timezone": 8, diff --git a/tests/test_relationship_event_class_evidence.py b/tests/test_relationship_event_class_evidence.py index 32f7f01b..c6acd53d 100644 --- a/tests/test_relationship_event_class_evidence.py +++ b/tests/test_relationship_event_class_evidence.py @@ -9,7 +9,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) -from jyotish_api_server import JyotishAPIHandler # noqa: E402 +from jyotish_api_server import JyotishComputeService # noqa: E402 from relationship_analysis import analyze_relationship # noqa: E402 from relationship_event_class_evidence import build_event_class_evidence # noqa: E402 @@ -74,7 +74,7 @@ def test_consultation_shaped_1990_chart_feeds_nonempty_event_class_hits() -> Non ) ) chart = golden["chart"] - handler = JyotishAPIHandler.__new__(JyotishAPIHandler) + handler = JyotishComputeService() dasha = (chart.get("modules") or {}).get("dasha") or {} dasha_info = handler._thematic_dasha_info(chart, dasha) assert dasha_info["maha_dasha"] == "Saturn" diff --git a/tests/test_thematic_dasha_info_source.py b/tests/test_thematic_dasha_info_source.py index ea4256cb..54a964d4 100644 --- a/tests/test_thematic_dasha_info_source.py +++ b/tests/test_thematic_dasha_info_source.py @@ -2,11 +2,11 @@ from __future__ import annotations -from scripts.jyotish_api_server import JyotishAPIHandler +from scripts.jyotish_api_server import JyotishComputeService -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() def test_prefers_dasha_sub_periods_current_over_birth_md() -> None: diff --git a/tests/test_vedastro_snapshot_cache.py b/tests/test_vedastro_snapshot_cache.py index 89a0d751..40c91883 100644 --- a/tests/test_vedastro_snapshot_cache.py +++ b/tests/test_vedastro_snapshot_cache.py @@ -11,7 +11,7 @@ import pytest from scripts import jyotish_api_server from scripts import vedastro_foreground as foreground -from scripts.jyotish_api_server import JyotishAPIHandler +from scripts.jyotish_api_server import JyotishComputeService from scripts.vedastro_snapshot_cache import ( lookup_snapshot, snapshot_cache_dir, @@ -21,8 +21,8 @@ from scripts.vedastro_snapshot_cache import ( ) -def _handler() -> JyotishAPIHandler: - return JyotishAPIHandler.__new__(JyotishAPIHandler) +def _handler() -> JyotishComputeService: + return JyotishComputeService() def _verified_gateway(*, request_id: str, reference_date: str) -> dict: @@ -56,7 +56,7 @@ def _fake_chart() -> dict: } -def _stub_local(monkeypatch, handler: JyotishAPIHandler, gateway) -> None: +def _stub_local(monkeypatch, handler: JyotishComputeService, gateway) -> None: monkeypatch.setattr(handler, "_compute_chart", lambda body: _fake_chart()) monkeypatch.setattr( handler,