fix: restore upstream sync consultation contracts
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## 2026-09-04 — 上游同步验收修复:咨询主题、finance 证据与模板门禁
|
||||
|
||||
咨询工作流重建 prompt pack 时保留既有 `evidence_snapshot`,避免 `strict_workflow_contracts` 丢失后把非原生主题从 `degraded` 误降为 `blocked`;真实 chart 未携带 engine snapshot 时复用服务端权威 `plan_route_contract()`,不伪造外部 oracle 成功。finance 采用 PyJHora Yogi Sphuta 标准式(Yogi=`Sun+Moon+93°20′`,Avayogi=`Yogi+186°40′`);D11 继续作为支持项,`wealth_promise_strength` 仍是主判据,缺 D11 不再把完整 Shadbala 的 `confidence_cap` 从 `medium-high` 压到 `low`。MCP 的公开路由保持 `finance`,明确财富问题不会因时间词先出现而漏装外部证据。
|
||||
|
||||
四个冻结模板不再引用仓内不存在的 MEVG 审计文件,分别改用现有的 transit、house-domain、prediction-boundary 与 mandatory-verification 规范。快速门 CORE 新增全域咨询、finance 严格证据、模板注册表和 VedAstro 外部证据四组回归测试。
|
||||
|
||||
## 2026-09-03 — health 三窗、专业参考导出与出生时间敏感度
|
||||
|
||||
health 咨询与个人报告现按压力窗、事件风险窗、恢复支持窗组织,必需证据覆盖 D6/D8/D30,Transit 缺证据时保留 blocked,并固定携带非医疗边界。报告中心新增确定性的 `professional_report_reference` JSON/Markdown 导出,不调用 writer,并净化 raw full-reading、代码路径和内部 oracle/module 标识。校正新增候选窗口自适应选题、请求级 `ayanamsa` / `node_mode`,以及只读的出生时间敏感度附录;默认仍为 Raman,既有评分与 confirmed 报告保持原字节。
|
||||
|
||||
@@ -7935,3 +7935,19 @@
|
||||
- 相关记录:无
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-515 | 缺少 D11 被误判为 finance 必需证据,完整 Shadbala 的 confidence cap 降为 low
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-09-03
|
||||
- 最近更新:2026-09-04
|
||||
- 影响面:`mcp_server.py::_collect_strict_evidence`、finance strict workflow
|
||||
- 用户现象:财富严格工作流在 Shadbala 分量完整、原本应保持 `medium-high` 置信度上限时,仅因没有 D11 Rudramsa 就返回 `confidence_cap=low`。
|
||||
- 触发条件:finance 证据包没有 `modules.varga_full.D11_Rudramsa`,其余财富主判据与完整 Shadbala 证据可用。
|
||||
- 根因:上游加入 `d11_rudramsa` 后,finance 的 `missing_evidence` 过滤表没有同步把它列为非阻断支持项;空 D11 因而被计入缺失的必需证据,触发通用 `missing -> low` 降级。
|
||||
- 修复:把 `d11_rudramsa` 加入 finance 非阻断证据集合。D11 仍可进入证据包,但只作支持项;`wealth_promise_strength` 保持主判据,完整 Shadbala 用例的 `medium-high` 断言不改。
|
||||
- 验证:聚焦回归集合 88 passed(31.71s);模板 validator `valid=true, problem_count=0, template_count=15`;本地 quick gate 为 Python CORE 585 passed / 1 skipped、前端 2628 passed、lint 0 errors、Next build 通过,退出 0(575.45s);`git diff --check` 通过。
|
||||
- 防复发:新增证据字段必须同时声明 required / supporting 角色;支持项不得因缺失进入 `missing_evidence`,也不得单独压低 confidence cap。
|
||||
- 相关记录:`TASK-upstream-sync-fix-20260903.md`
|
||||
- 复发自:`45d13258` / `f2241463` 上游同步批次
|
||||
- 修复版本:待发布
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# PROGRESS · 上游同步验收修复单(2026-09-04)
|
||||
|
||||
任务书:`TASK-upstream-sync-fix-20260903.md`
|
||||
工作树:`/Users/jesse/Downloads/Copse/astrology/.worktrees/upstream-sync-fix-20260903`
|
||||
分支:`codex/upstream-sync-fix-20260903`
|
||||
实际开工基线:`origin/staging` @ `779717f48c7ce08f9c731663f2203dae19c250c3`
|
||||
原任务书验收基线:`origin/staging` @ `c1e3f32a`
|
||||
|
||||
当前状态:本地修复与验证完成,改动仍在独立工作树且未提交。本记录未声明提交、推送或部署完成。
|
||||
|
||||
## 1. 非原生主题恢复 `degraded`
|
||||
|
||||
`scripts/jyotish_api_server.py` 重建 `ai_prompt_pack` 时,先合并既有 `evidence_snapshot`,再刷新本次请求的 birth / ayanamsa / core 等字段。这样不会丢掉上游已装配的 `strict_workflow_contracts`,报告适配器仍可识别 `upstream_contract_available=true`。
|
||||
|
||||
预期合同:
|
||||
|
||||
- `general` / `timing` / `family` / `education` / `migration` / `annual`:`degraded`;
|
||||
- 上述主题的 adapter:`upstream_contract_available=true`;
|
||||
- `career` / `marriage` / `wealth` / `health`:继续为 `supported`。
|
||||
|
||||
真实 HTTP 十主题 smoke 全部返回 HTTP 200:`career` / `marriage` / `wealth` / `health` 为 `supported`;`education` / `migration` / `family` / `annual` / `timing` / `general` 为 `degraded`,且六个非原生主题的 `upstream_contract_available=true`。耗时 70.82s,脱敏结果保存于 `/tmp/upstream-sync-fix-http-smoke.json`;本地 API 已停止。
|
||||
|
||||
## 2. finance 严格证据口径
|
||||
|
||||
### Yogi / Avayogi 断言取舍
|
||||
|
||||
| 原值 | 新值 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| Yogi 点公式为 `Sun + Moon` | `Sun + Moon + 93°20′`;`formula_profile=pyjhora_yogi_sphuta_v1` | 接受任务书决策:旧公式漏掉 Pushya 起点常量,改为 PyJHora Yogi Sphuta 标准式。 |
|
||||
| Avayogi 由旧 Yogi 点派生 | `Yogi + 186°40′` | 与新的标准 Yogi Sphuta 点配套计算。 |
|
||||
| 示例 Yogi 点 `20° / Bharani / 1宫` | `113.3333° / Ashlesha / 4宫`;Avayogi 点锁定 `300°` | 同一虚构夹具按标准公式重算后的确定性结果,不保留旧错误期望。 |
|
||||
| `Yogi=Venus`、`duplicate_yogi=Mars`、`Avayogi=Saturn` | `Yogi=Mercury`、`duplicate_yogi=Moon`、`Avayogi=Mars` | 点位重算后对应星曜随之变化。 |
|
||||
| `level=strong`,存在 `lagna_yogi_tight_orb`、`yogi_planet_is_2l` 和三条 strong signals | `level=weak`,距离 `93.3333°`,tight-orb / wealth-lord links / signals 为空 | 新点位不再满足旧强钩子条件,弱 Yogi 不得抬高财富主判据或写入 `yogi_active`。 |
|
||||
| `primary_source=dhana_yogi_hooks`、source diversity `2`、支持源 `dhana+yogi` | `primary_source=dhana_yogas`、source diversity `1`、支持源仅 `dhana` | Yogi 仅作 modifier;该夹具的新 Yogi 为 weak,不能计作独立财富支持源。 |
|
||||
|
||||
### D11 与 confidence cap
|
||||
|
||||
- `d11_rudramsa` 保留在 finance 证据包中,但定位为**支持项**,不是严格证据的必需项,也不是主判据。
|
||||
- `wealth_promise_strength` 继续作为财富承诺的主判据。
|
||||
- `confidence_cap` 回归根因:finance 的 `missing_evidence` 过滤表未把新增的 `d11_rudramsa` 列入非阻断证据。结果是没有 D11 时,即使 Shadbala 分量完整,也会被误判为缺必需证据并把 cap 从 `medium-high` 压到 `low`。
|
||||
- 修复:把 `d11_rudramsa` 纳入非阻断集合;不改完整 Shadbala 用例原有的 `medium-high` 断言。
|
||||
- MCP 公开边界继续使用 `finance`:统一路由器内部 canonical `wealth` 会映射回公开名称;当 `annual` / `timing` 时间词先命中、但问题同时有明确财富关键词时,在 MCP strict 边界以 `wealth` 重新解析,避免漏装 finance 的 VedAstro / external evidence。
|
||||
- 旧 source-pack 元数据恢复到当前 Next.js 真路径:`frontend_interpretation_layer` 指向 report interpretation packs,`frontend_planet_house_details` 指向 `personal-report-generation.ts`,覆盖仍为 `9_planets_x_12_houses`。
|
||||
|
||||
对应 Bug:`BUG-515`。
|
||||
|
||||
## 3. 模板注册表引用取舍
|
||||
|
||||
没有复制缺失的 `references/mevg_chinese_full_layer_and_practice_audit_2026_08_26.md`;四个模板保留,改指向仓内已有且语义直接匹配的规范:
|
||||
|
||||
| 模板 | 新引用 | 取舍理由 |
|
||||
| --- | --- | --- |
|
||||
| `dual_luminary_timing_context` | `references/transit-comprehensive-guide.md` | 模板要求 Moon、Lagna/Sun、Dasha 与 transit 的交叉检查,综合行运指南比缺失的通用审计文档更直接。 |
|
||||
| `seventh_house_topic_split` | `references/house-domain-planet-mapping.md` | 模板核心是区分婚恋、合同、公众与对手等七宫主题,宫位—领域映射是本仓直接真源。 |
|
||||
| `empty_window_not_success` | `references/prediction-boundary-protocol.md` | “没有阻碍不等于成功”属于预测边界与置信度约束,应由边界协议承载。 |
|
||||
| `dasha_boundary_not_event_date` | `references/mandatory-verification-gate-protocol.md` | 换运边界不能直接写成事件日,需要多重 Dasha、transit、命盘承诺及外部验证门共同收敛。 |
|
||||
|
||||
其余 `source_refs` 保留不变。
|
||||
|
||||
## 4. quick CORE 扩列
|
||||
|
||||
`scripts/run_quality_gate.py` 的 `CORE_PYTEST_TARGETS` 新增:
|
||||
|
||||
- `tests/test_consultation_workflow_domains.py`
|
||||
- `tests/test_mcp_strict_workflow_finance.py`
|
||||
- `tests/test_interpretation_template_registry.py`
|
||||
- `tests/test_vedastro_external_technique_evidence.py`
|
||||
|
||||
目的:让本次发现的主题合同、finance 严格证据、模板引用与 VedAstro 外部证据回归进入自动 staging 快速门,而不是只在全量测试中暴露。
|
||||
|
||||
## 5. 本地验证实证
|
||||
|
||||
### 聚焦 pytest
|
||||
|
||||
以下集合共收集并通过 88 条测试,墙钟耗时 31.71s:
|
||||
|
||||
- `tests/test_consultation_workflow_domains.py`:31 条;
|
||||
- `tests/test_mcp_strict_workflow_finance.py`:37 条;
|
||||
- `tests/test_interpretation_template_registry.py`:2 条;
|
||||
- `tests/test_vedastro_external_technique_evidence.py`:10 条;
|
||||
- `tests/test_api_server_growth_contract.py`:3 条;
|
||||
- `tests/test_consultation_engine_field_bridge.py`:5 条。
|
||||
|
||||
### 模板 validator
|
||||
|
||||
`scripts/validate_interpretation_templates.py` 退出 0:`valid=true`、`problem_count=0`、`template_count=15`(约 0.04s)。
|
||||
|
||||
### quick gate
|
||||
|
||||
首次运行因独立 worktree 没有安装前端依赖而在 `npm test` 报 `tsx: command not found`;执行 `npm ci` 后重跑通过。没有运行 `npm audit fix`,避免把依赖升级混入本修复单;`frontend/node_modules/` 受 `.gitignore` 排除,不是交付文件。
|
||||
|
||||
最终结果:
|
||||
|
||||
- Python CORE:585 passed、1 skipped、183 warnings;
|
||||
- 前端测试:2628 passed、0 failed、0 skipped;
|
||||
- lint:0 errors、74 warnings;
|
||||
- Next build:通过;
|
||||
- `scripts/run_quality_gate.py --profile quick`:退出 0,墙钟 575.45s。
|
||||
|
||||
完整日志:`/tmp/upstream-sync-fix-quick-gate.log`。
|
||||
|
||||
### 文件守卫
|
||||
|
||||
- `scripts/jyotish_api_server.py`:11200 行,与 `HEAD` 同文件行数一致,满足“不增长”红线;
|
||||
- `git diff --check`:通过;
|
||||
- `frontend/node_modules/`:无 Git tracked 文件。
|
||||
|
||||
## 交付边界
|
||||
|
||||
- 本工作树改动目前**未提交**。
|
||||
- 未 push 到任何远端;`origin/staging` 未因本工作树前进。
|
||||
- 未触发 CI、迁移或部署。
|
||||
- 未检查或声称 staging `/api/health` 已包含本修复。
|
||||
@@ -99,7 +99,7 @@
|
||||
| 任务书 | 进度 | 主题 | 状态 | 落点 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `TASK-upstream-sync-20260903.md` | `PROGRESS-upstream-sync-20260903.md`、`PROGRESS-upstream-capabilities-20260903.md` | 上游快照推进到 `a6f47abd`、引擎三方合并、health 三窗 / VedAstro REST 桥 / 专业报告导出 / 新字段接聊天与报告 / 校正解冻三项 | 已验收(带修复单) | 0/1/2/4/6:`45d13258`、`f2241463`、`734d2059`;3/5/7:`c2f23131`~`6063c1f7`(BUG-514)。staging 已部署 `6063c1f7`。未过项全在 `TASK-upstream-sync-fix-20260903.md` |
|
||||
| `TASK-upstream-sync-fix-20260903.md` | — | 非原生主题合同回 `degraded`(**P1,已随 6063c1f7 上 staging:timing/general 主题回执 blocked**)、finance 严格证据口径与 confidence_cap(7 红)、模板注册表缺引用(4 problems)、vedastro 外部证据 4 红、门禁 CORE 扩列 | 待领取 | 分支 `codex/upstream-sync-fix-20260903`;2026-09-04 复核:任务 3/5/7 合入后本单各项仍原样 |
|
||||
| `TASK-upstream-sync-fix-20260903.md` | `PROGRESS-upstream-sync-fix-20260903.md` | 非原生主题恢复 `degraded`、finance Yogi/D11/confidence cap、模板注册表引用、VedAstro/MCP finance 路由与 quick CORE 扩列 | 本地验证完成 | 独立 worktree 基线 `779717f4`;BUG-515。88 条聚焦回归与 quick gate 通过,十主题 HTTP smoke 符合合同;未提交、未推送、未部署 |
|
||||
|
||||
## 命名与归档
|
||||
|
||||
|
||||
+40
-3
@@ -500,6 +500,11 @@ def _existing_interpretation_source_pack() -> Dict[str, Any]:
|
||||
promote_batch2_paths = PROMOTE_BATCH2_TOPIC_SOURCE_REFS
|
||||
reference_only_paths = REFERENCE_ONLY_CONFLICT_SOURCE_REFS
|
||||
blocked_non_runtime_paths = BLOCKED_NON_RUNTIME_SOURCE_REFS
|
||||
frontend_interpretation_paths = [
|
||||
"frontend/src/lib/report-interpretation-packs/general.ts",
|
||||
"frontend/src/lib/report-interpretation-packs/themes.ts",
|
||||
]
|
||||
planet_house_paths = ["frontend/src/lib/personal-report-generation.ts"]
|
||||
yoga_rule_paths = [
|
||||
core_yogas_path,
|
||||
"references/yoga_rules.json",
|
||||
@@ -533,6 +538,8 @@ def _existing_interpretation_source_pack() -> Dict[str, Any]:
|
||||
*REAL_CASE_STUDIES_BATCH1_SOURCE_REFS,
|
||||
*RISHI_AI_MCP_BATCH1_SOURCE_REFS,
|
||||
*VEDIC_ASTRO_SKILLS_BATCH1_SOURCE_REFS,
|
||||
*frontend_interpretation_paths,
|
||||
*planet_house_paths,
|
||||
qa_rules_path,
|
||||
reader_chart_rules_path,
|
||||
reader_validation_rules_path,
|
||||
@@ -564,6 +571,7 @@ def _existing_interpretation_source_pack() -> Dict[str, Any]:
|
||||
"reference_only_conflict_sources",
|
||||
"qa_governance_rules",
|
||||
"reader_validation_rules",
|
||||
"frontend_interpretation_layer",
|
||||
"yoga_rule_layer",
|
||||
"saham_rule_layer",
|
||||
],
|
||||
@@ -655,6 +663,18 @@ def _existing_interpretation_source_pack() -> Dict[str, Any]:
|
||||
"next_batches": REMAINING_PRIORITY1_BATCH_QUEUE,
|
||||
"boundary": "Future batches remain audit-only until classified and tested.",
|
||||
},
|
||||
"frontend_interpretation_layer": {
|
||||
"status": "available" if all(_repo_relative_exists(path) for path in frontend_interpretation_paths) else "partial",
|
||||
"source_refs": frontend_interpretation_paths,
|
||||
"promotion_status": "reference_layer",
|
||||
},
|
||||
"frontend_planet_house_details": {
|
||||
"status": "available" if all(_repo_relative_exists(path) for path in planet_house_paths) else "partial",
|
||||
"coverage": "9_planets_x_12_houses",
|
||||
"planet_count": 9,
|
||||
"house_count": 12,
|
||||
"source_refs": planet_house_paths,
|
||||
},
|
||||
"qa_governance_layer": {
|
||||
"status": "available" if _repo_relative_exists(qa_rules_path) else "partial",
|
||||
"source_refs": [qa_rules_path],
|
||||
@@ -3930,7 +3950,7 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
present["functional_benefic_malefic"] = _derive_functional_benefic_malefic(modules)
|
||||
present["interpretation_source_pack"] = _existing_interpretation_source_pack()
|
||||
missing = [key for key, value in present.items() if key not in {
|
||||
"chart", "external_activation", "external_technique_evidence", "vedastro_official_snapshot", "source_priority", "dignity_guardrail", "gains_convergence", "career_convergence", "avayogi_risk", "ashtakavarga_finance_support", "shadbala_component_audit", "asc_sign", "pav_finance_support", "sodhita_finance_support", "kakshya_finance_support", "functional_benefic_malefic", "interpretation_source_pack", "d4_turyamsa", "d16_shodasamsa", "d24_siddhamsa", "bhava_bala", "hora_lagna", "indu_lagna", "sree_lagna", "dhan_saham", "artha_saham", "labha_saham"
|
||||
"chart", "external_activation", "external_technique_evidence", "vedastro_official_snapshot", "source_priority", "dignity_guardrail", "gains_convergence", "career_convergence", "avayogi_risk", "ashtakavarga_finance_support", "shadbala_component_audit", "asc_sign", "pav_finance_support", "sodhita_finance_support", "kakshya_finance_support", "functional_benefic_malefic", "interpretation_source_pack", "d4_turyamsa", "d11_rudramsa", "d16_shodasamsa", "d24_siddhamsa", "bhava_bala", "hora_lagna", "indu_lagna", "sree_lagna", "dhan_saham", "artha_saham", "labha_saham"
|
||||
} and value in (None, {}, [], "")]
|
||||
convergence_hits: List[Dict[str, Any]] = [
|
||||
item for item in [
|
||||
@@ -4632,6 +4652,17 @@ def strict_workflow(
|
||||
JSON with routed analysis and confidence level
|
||||
"""
|
||||
route_packet = _UNIFIED_CONSULTATION_ORCHESTRATOR.resolve_route(question, None)
|
||||
mcp_declared_route = None
|
||||
question_text = (question or "").lower()
|
||||
if route_packet["question_type"] in {"annual", "timing"} and any(
|
||||
alias in question_text
|
||||
for alias, domain in _UNIFIED_CONSULTATION_ORCHESTRATOR._THEME_ALIASES.items()
|
||||
if domain == "wealth"
|
||||
):
|
||||
mcp_declared_route = "wealth"
|
||||
route_packet = _UNIFIED_CONSULTATION_ORCHESTRATOR.resolve_route(
|
||||
question, None, declared_route=mcp_declared_route
|
||||
)
|
||||
normalized_themes = _UNIFIED_CONSULTATION_ORCHESTRATOR.normalize_themes(route_packet["primary_theme"])
|
||||
|
||||
result = _execute_mcp_consultation_workflow(
|
||||
@@ -4653,12 +4684,18 @@ def strict_workflow(
|
||||
western_oracle_payload=western_oracle_payload,
|
||||
)
|
||||
chart = result.get("chart") if isinstance(result, dict) else {}
|
||||
route = _safe_get(result, "routing", "question_type") or route_packet["question_type"] or "general"
|
||||
route = mcp_declared_route or _safe_get(result, "routing", "question_type") or route_packet["question_type"] or "general"
|
||||
route_surface = _UNIFIED_CONSULTATION_ORCHESTRATOR.route_surface_contract(route)
|
||||
route = "finance" if route_surface["runtime_route"] == "wealth" else route
|
||||
if isinstance(result, dict):
|
||||
routing = result.get("routing") if isinstance(result.get("routing"), dict) else {}
|
||||
result["routing"] = {**route_packet, **routing, **route_surface}
|
||||
result["routing"] = {**route_packet, **routing, **route_surface, "question_type": route}
|
||||
result["route_normalization"] = route_surface
|
||||
for key in ("unified_orchestrator", "runtime_planner"):
|
||||
metadata = result.get(key)
|
||||
if isinstance(metadata, dict):
|
||||
metadata_route = metadata.get("route") if isinstance(metadata.get("route"), dict) else {}
|
||||
result[key] = {**metadata, "route": {**metadata_route, "question_type": route}}
|
||||
planner = result.get("runtime_planner") if isinstance(result.get("runtime_planner"), dict) else {}
|
||||
result["execution_receipt"] = _build_execution_receipt(chart, planner)
|
||||
if isinstance(chart, dict) and "error" not in chart:
|
||||
|
||||
@@ -425,7 +425,7 @@
|
||||
"authority_level": "B/C guarded",
|
||||
"domain": ["timing", "transit", "dasha", "moon", "lagna"],
|
||||
"source_refs": [
|
||||
"references/mevg_chinese_full_layer_and_practice_audit_2026_08_26.md",
|
||||
"references/transit-comprehensive-guide.md",
|
||||
"references/mandatory-verification-gate-protocol.md",
|
||||
"references/strict-workflow-router.md"
|
||||
],
|
||||
@@ -461,7 +461,7 @@
|
||||
"authority_level": "B/C guarded",
|
||||
"domain": ["relationship", "contract", "public", "opponent", "house_judgment"],
|
||||
"source_refs": [
|
||||
"references/mevg_chinese_full_layer_and_practice_audit_2026_08_26.md",
|
||||
"references/house-domain-planet-mapping.md",
|
||||
"references/event_judgment_marriage.md",
|
||||
"references/strict-workflow-router.md"
|
||||
],
|
||||
@@ -498,7 +498,7 @@
|
||||
"authority_level": "B/C guarded",
|
||||
"domain": ["prediction_boundary", "timing", "transit", "dasha"],
|
||||
"source_refs": [
|
||||
"references/mevg_chinese_full_layer_and_practice_audit_2026_08_26.md",
|
||||
"references/prediction-boundary-protocol.md",
|
||||
"references/prediction-output-protocol.md",
|
||||
"references/mandatory-verification-gate-protocol.md"
|
||||
],
|
||||
@@ -534,7 +534,7 @@
|
||||
"authority_level": "B/C guarded",
|
||||
"domain": ["dasha", "timing", "event_prediction", "sandhi"],
|
||||
"source_refs": [
|
||||
"references/mevg_chinese_full_layer_and_practice_audit_2026_08_26.md",
|
||||
"references/mandatory-verification-gate-protocol.md",
|
||||
"references/dasa-convergence-methodology.md",
|
||||
"assets/timing-prediction-template.md"
|
||||
],
|
||||
|
||||
@@ -2177,14 +2177,14 @@ def execute_consultation_workflow(
|
||||
from scripts.consultation_plan_contract import (
|
||||
ConsultationPlanContractError,
|
||||
apply_plan_precision_boundary,
|
||||
declared_workflow_route,
|
||||
declared_workflow_route, plan_route_contract,
|
||||
validate_consultation_plan_contract,
|
||||
)
|
||||
except ModuleNotFoundError: # pragma: no cover - direct script execution
|
||||
from consultation_plan_contract import (
|
||||
ConsultationPlanContractError,
|
||||
apply_plan_precision_boundary,
|
||||
declared_workflow_route,
|
||||
declared_workflow_route, plan_route_contract,
|
||||
validate_consultation_plan_contract,
|
||||
)
|
||||
try:
|
||||
@@ -2338,9 +2338,9 @@ def execute_consultation_workflow(
|
||||
modules = chart.get('modules') if isinstance(chart.get('modules'), dict) else {}
|
||||
prompt_snapshot = (((chart.get('ai_prompt_pack') or {}).get('evidence_snapshot')) or {}) if isinstance(chart, dict) else {}
|
||||
strict_workflow_contracts = prompt_snapshot.get('strict_workflow_contracts') if isinstance(prompt_snapshot.get('strict_workflow_contracts'), dict) else {}
|
||||
route_contracts = {theme: plan_route_contract(theme) for theme in themes}
|
||||
chart_guided_topics = modules.get('guided_topics') if isinstance(modules.get('guided_topics'), list) else []
|
||||
audited_remedies = handler._build_audited_remedies_from_guided_topics(chart_guided_topics)
|
||||
|
||||
thematic_report = {}
|
||||
if 'run_thematic_report' in runtime_planner.get('sync_steps', []):
|
||||
thematic_report = handler._compute_thematic_report({
|
||||
@@ -2354,12 +2354,11 @@ def execute_consultation_workflow(
|
||||
'skip_full_reading_for_thematic': True,
|
||||
'upstream_contract': {
|
||||
'chart': chart_for_theme,
|
||||
'strict_workflow_contracts': strict_workflow_contracts,
|
||||
'strict_workflow_contracts': strict_workflow_contracts, 'route_contracts': route_contracts,
|
||||
'guided_topics': chart_guided_topics,
|
||||
},
|
||||
})
|
||||
executed_steps.append('run_thematic_report')
|
||||
|
||||
vedastro_gateway = rectification.get('vedastro_gateway') if isinstance(rectification, dict) else None
|
||||
if defer_optional_external_evidence:
|
||||
vedastro_gateway = _join_foreground_vedastro(
|
||||
@@ -4282,6 +4281,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
adapter_evidence = {}
|
||||
derived_context = None
|
||||
strict_workflow_contracts = upstream_contract.get('strict_workflow_contracts') if isinstance(upstream_contract.get('strict_workflow_contracts'), dict) else {}
|
||||
route_contracts = upstream_contract.get('route_contracts') if isinstance(upstream_contract.get('route_contracts'), dict) else {}
|
||||
upstream_guided_topics = upstream_contract.get('guided_topics') if isinstance(upstream_contract.get('guided_topics'), list) else []
|
||||
if not has_custom_evidence and not strict_workflow_contracts and self._can_derive_thematic_evidence(chart_data):
|
||||
derived_context = self._derive_thematic_evidence(chart_data, report_orchestrator)
|
||||
@@ -4349,9 +4349,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
theme_name,
|
||||
evidence=adapter_evidence.get(theme_name, []),
|
||||
upstream_contract_available=self._thematic_upstream_contract_available(
|
||||
strict_workflow_contracts,
|
||||
theme_name,
|
||||
report_orchestrator,
|
||||
strict_workflow_contracts, route_contracts,
|
||||
theme_name, report_orchestrator,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5179,10 +5178,11 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def _thematic_upstream_contract_available(
|
||||
self,
|
||||
strict_workflow_contracts,
|
||||
domain,
|
||||
report_orchestrator,
|
||||
strict_workflow_contracts, route_contracts,
|
||||
domain, report_orchestrator,
|
||||
):
|
||||
if isinstance(route_contracts, dict) and bool(route_contracts.get(domain)):
|
||||
return True
|
||||
if not isinstance(strict_workflow_contracts, dict):
|
||||
return False
|
||||
for raw_domain, contract in strict_workflow_contracts.items():
|
||||
@@ -7182,7 +7182,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'mode': 'jyotish_structured_prompt_pack',
|
||||
'prompt_zh': '\n'.join(prompt_lines),
|
||||
'evidence_snapshot': {
|
||||
'birth': birth,
|
||||
**((chart.get('ai_prompt_pack') or {}).get('evidence_snapshot') or {}), 'birth': birth,
|
||||
'ayanamsa': {
|
||||
'name': birth.get('ayanamsa_name', DEFAULT_AYANAMSA_NAME),
|
||||
'display': ayanamsa_display,
|
||||
|
||||
@@ -77,6 +77,11 @@ CORE_PYTEST_TARGETS = [
|
||||
"tests/test_api_server_growth_contract.py",
|
||||
# Fail-fast heavy-compute concurrency gate (429 + Retry-After, health ungated).
|
||||
"tests/test_api_heavy_compute_gate.py",
|
||||
# Upstream-sync acceptance regressions must fail the automatic staging gate.
|
||||
"tests/test_consultation_workflow_domains.py",
|
||||
"tests/test_mcp_strict_workflow_finance.py",
|
||||
"tests/test_interpretation_template_registry.py",
|
||||
"tests/test_vedastro_external_technique_evidence.py",
|
||||
]
|
||||
|
||||
RUNTIME_TRUTH_PYTEST_TARGETS = [
|
||||
|
||||
@@ -22,7 +22,7 @@ def _handler() -> JyotishAPIHandler:
|
||||
return JyotishAPIHandler.__new__(JyotishAPIHandler)
|
||||
|
||||
|
||||
def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler) -> None:
|
||||
def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler, *, include_strict_contracts: bool = True) -> None:
|
||||
chart = {
|
||||
"success": True,
|
||||
"birth_info": {"date": "1997-08-08", "time": "05:00", "tz": 8},
|
||||
@@ -34,7 +34,7 @@ def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler) -> None:
|
||||
"strict_workflow_contracts": {
|
||||
domain: {"status": "available", "domain": domain}
|
||||
for domain in CANONICAL_DOMAINS
|
||||
}
|
||||
} if include_strict_contracts else {}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -109,6 +109,26 @@ def test_complete_consultation_workflow_handles_every_canonical_domain(
|
||||
assert "No general-theme fallback" in domain_report["boundary"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("domain", ["education", "migration", "family", "annual", "timing", "general"])
|
||||
def test_non_native_domains_reuse_server_route_contract_without_engine_snapshot(
|
||||
monkeypatch,
|
||||
domain: str,
|
||||
) -> None:
|
||||
handler = _handler()
|
||||
_stub_consultation_runtime(monkeypatch, handler, include_strict_contracts=False)
|
||||
|
||||
result = handler._compute_consultation_workflow({
|
||||
"entry_mode": "direct_chart", "question": domain, "theme": [domain],
|
||||
"year": 2000, "month": 1, "day": 1, "hour": 12, "minute": 0,
|
||||
"lat": 0.0, "lon": 0.0, "tz": 0.0, "western_mode": False,
|
||||
"defer_optional_external_evidence": True,
|
||||
})
|
||||
|
||||
report = result["thematic_report"]["themes"][domain]
|
||||
assert report["status"] == "degraded"
|
||||
assert report["thematic_adapter"]["upstream_contract_available"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("domain", CANONICAL_DOMAINS)
|
||||
def test_versioned_plan_domains_execute_their_declared_route_not_the_question_text_route(
|
||||
monkeypatch,
|
||||
|
||||
@@ -1105,20 +1105,21 @@ def test_derive_yogi_wealth_support_detects_strong_native_hook() -> None:
|
||||
}
|
||||
support = _derive_yogi_wealth_support(modules)
|
||||
assert support is not None
|
||||
assert support["level"] == "strong"
|
||||
assert support["level"] == "weak"
|
||||
assert support["source"] == "yogi_asc_tight_orb_wealth"
|
||||
assert support["yogi_planet"] == "Venus"
|
||||
assert support["duplicate_yogi"] == "Mars"
|
||||
assert support["avayogi"] == "Saturn"
|
||||
assert support["yogi_point_house"] == 1
|
||||
assert support["tight_orb_hits"] == ["lagna_yogi_tight_orb"]
|
||||
assert support["wealth_lord_links"] == ["yogi_planet_is_2l"]
|
||||
assert support["formula_profile"] == "pyjhora_yogi_sphuta_v1"
|
||||
assert support["yogi_planet"] == "Mercury"
|
||||
assert support["duplicate_yogi"] == "Moon"
|
||||
assert support["avayogi"] == "Mars"
|
||||
assert support["yogi_point_longitude"] == 113.3333
|
||||
assert support["avayogi_point_longitude"] == 300.0
|
||||
assert support["yogi_point_nakshatra"] == "Ashlesha"
|
||||
assert support["yogi_point_house"] == 4
|
||||
assert support["lagna_yogi_distance_deg"] == 93.3333
|
||||
assert support["tight_orb_hits"] == []
|
||||
assert support["wealth_lord_links"] == []
|
||||
assert support["risk_flags"] == []
|
||||
assert support["signals"] == [
|
||||
"yogi_planet_in_wealth_house",
|
||||
"yogi_planet_is_2l",
|
||||
"lagna_yogi_tight_orb",
|
||||
]
|
||||
assert support["signals"] == []
|
||||
|
||||
|
||||
def test_wealth_folding_dhana_keeps_yogi_quiet_when_native_support_is_weak() -> None:
|
||||
@@ -1165,11 +1166,10 @@ def test_wealth_folding_adds_native_yogi_support_only_when_base_promise_exists()
|
||||
}
|
||||
res = _derive_wealth_promise_strength(modules)
|
||||
assert res["level"] == "moderate"
|
||||
assert res["primary_source"] == "dhana_yogi_hooks"
|
||||
assert res["source_diversity"] == 2
|
||||
assert res["supporting_sources"] == ["dhana", "yogi"]
|
||||
assert res["yogi_support"]["level"] == "strong"
|
||||
assert res["yogi_support"]["wealth_lord_links"] == ["yogi_planet_is_2l"]
|
||||
assert res["primary_source"] == "dhana_yogas"
|
||||
assert res["source_diversity"] == 1
|
||||
assert res["supporting_sources"] == ["dhana"]
|
||||
assert res["yogi_support"] is None
|
||||
|
||||
|
||||
def test_wealth_folding_yogi_only_is_blocked_without_base_promise() -> None:
|
||||
@@ -1227,31 +1227,13 @@ def test_collect_strict_evidence_finance_adds_native_yogi_hook_without_external_
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"]["wealth_promise_strength"] == {
|
||||
"level": "moderate",
|
||||
"primary_source": "dhana_yogi_hooks",
|
||||
"primary_source": "dhana_yogas",
|
||||
"count": 1,
|
||||
"source_diversity": 2,
|
||||
"supporting_sources": ["dhana", "yogi"],
|
||||
"yogi_support": {
|
||||
"avayogi": "Saturn",
|
||||
"duplicate_yogi": "Mars",
|
||||
"lagna_yogi_distance_deg": 0.0,
|
||||
"level": "strong",
|
||||
"risk_flags": [],
|
||||
"signals": [
|
||||
"yogi_planet_in_wealth_house",
|
||||
"yogi_planet_is_2l",
|
||||
"lagna_yogi_tight_orb",
|
||||
],
|
||||
"source": "yogi_asc_tight_orb_wealth",
|
||||
"tight_orb_hits": ["lagna_yogi_tight_orb"],
|
||||
"wealth_lord_links": ["yogi_planet_is_2l"],
|
||||
"yogi_planet": "Venus",
|
||||
"yogi_point_house": 1,
|
||||
"yogi_point_longitude": 20.0,
|
||||
"yogi_point_nakshatra": "Bharani",
|
||||
},
|
||||
"source_diversity": 1,
|
||||
"supporting_sources": ["dhana"],
|
||||
"yogi_support": None,
|
||||
}
|
||||
assert "yogi_active" in strict["event_judgement"]["secondary_context"]
|
||||
assert "yogi_active" not in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_keeps_native_yogi_quiet_when_support_is_weak() -> None:
|
||||
|
||||
Reference in New Issue
Block a user