From ba867a4b8caf66224f115ab95e7b52fafced3188 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 28 Jun 2026 10:32:10 +0800 Subject: [PATCH] Enforce external-truth Yogi finance boundary --- ...n_card_finance_yogi_boundary_2026_06_28.md | 70 ++++++++++++++++++ ...pass_external_truth_boundary_2026_06_28.md | 74 +++++++++++++++++++ mcp_server.py | 65 ++++++++++------ tests/test_mcp_strict_workflow_finance.py | 73 +++++++++++++++++- 4 files changed, 259 insertions(+), 23 deletions(-) create mode 100644 docs/research/git_execution_card_finance_yogi_boundary_2026_06_28.md create mode 100644 docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md diff --git a/docs/research/git_execution_card_finance_yogi_boundary_2026_06_28.md b/docs/research/git_execution_card_finance_yogi_boundary_2026_06_28.md new file mode 100644 index 00000000..836af199 --- /dev/null +++ b/docs/research/git_execution_card_finance_yogi_boundary_2026_06_28.md @@ -0,0 +1,70 @@ +# Git Execution Card - Finance Yogi Boundary Only + +日期:2026-06-28 +目标:只提交 finance adjudicator 的外部真值 Yogi 边界修复,不混入其他工作树噪音。 + +## 目标文件 + +1. `mcp_server.py` +2. `tests/test_mcp_strict_workflow_finance.py` +3. `docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md` + +## 执行顺序 + +### Step 1 - 确认暂存范围 + +```bash +git -C /Users/wuyongnaren/Documents/印度占星 reset +git -C /Users/wuyongnaren/Documents/印度占星 add -- \ + mcp_server.py \ + tests/test_mcp_strict_workflow_finance.py \ + docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md +``` + +### Step 2 - 检查暂存区 + +```bash +git -C /Users/wuyongnaren/Documents/印度占星 diff --cached --name-only +git -C /Users/wuyongnaren/Documents/印度占星 diff --cached --stat +git -C /Users/wuyongnaren/Documents/印度占星 diff --cached --check +``` + +预期:只看到上述 3 个文件。 + +### Step 3 - 回归验证 + +```bash +python3 -m pytest /Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_finance.py -q +``` + +预期: + +- `12 passed` + +### Step 4 - 独立提交 + +建议提交语义: + +```bash +git -C /Users/wuyongnaren/Documents/印度占星 commit -m "Enforce external-truth Yogi finance boundary" +``` + +### Step 5 - 推送当前分支 + +```bash +git -C /Users/wuyongnaren/Documents/印度占星 push origin codex/release-hygiene-ci +``` + +## 禁止事项 + +这次提交不要混入: + +- `docs/research/public_benchmark_dashboard_latest.md` +- `references/oracle/artifacts/pyjhora_oracle_artifact_manifest.json` +- 任意 `patch_*.py` +- 任意本地试验脚本 +- 未整理的 sidecar 研究包 + +## 一句话判断 + +**这是一张“只交 finance Yogi 真实性边界,不交别的”的推送卡。** diff --git a/docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md b/docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md new file mode 100644 index 00000000..515562e0 --- /dev/null +++ b/docs/research/wealth_adjudicator_fifth_pass_external_truth_boundary_2026_06_28.md @@ -0,0 +1,74 @@ +# Wealth Adjudicator Fifth Pass External Truth Boundary (2026-06-28) + +## What Was Fixed + +This pass closed a structural contradiction in the finance adjudicator: + +- one branch of the codebase was already moving toward `external_truth.yogi_planet` +- another branch was still internally deriving Yogi from Sun/Moon degrees and letting that influence finance promise folding + +That violated the new high-rigor boundary. + +## Boundary Now Enforced + +The finance adjudicator now follows this rule: + +1. No external `yogi_planet` -> no Yogi contribution +2. External `yogi_planet` present -> lightweight D1 gate only +3. Only then may Yogi enter: + - `wealth_promise_strength.supporting_sources` + - `source_diversity` + - `secondary_context` as `yogi_active` + +## Removed Risk + +The following behavior is no longer allowed: + +- deriving Yogi from internal Sun/Moon degrees +- upgrading `dhana_yogas` to `dhana_yogi_hooks` without upstream truth +- creating `yogi_support_only` from local chart math + +## Regression Contract + +The finance regression suite now locks these four truths: + +1. `dhana` alone must stay `dhana_yogas` without external truth +2. `dhana + lakshmi` must stay `dhana_lakshmi_hooks` without external truth +3. Yogi-only local chart inputs must not produce a finance promise +4. External truth + valid Kendra/Trikona placement may promote to `dhana_yogi_hooks` + +## Verification + +Command: + +```bash +python3 -m pytest tests/test_mcp_strict_workflow_finance.py -q +``` + +Observed: + +- `12 passed` + +Manual probe confirmed: + +- `NO_EXTERNAL` -> no Yogi source in promise folding +- `WITH_EXTERNAL` -> `supporting_sources = ["dhana", "yogi"]` +- `secondary_context` includes `yogi_active` + +## Why This Matters + +This is not just a bug fix. It restores honesty at the adjudication layer. + +The finance engine can now say: + +- "Yogi affected this judgement because an upstream truth source supplied it" + +but can no longer silently smuggle Yogi into the result from local chart math. + +## Next Step + +Keep the same discipline for all future high-order promise hooks: + +1. external truth first +2. lightweight downstream gating second +3. no hidden internal recomputation diff --git a/mcp_server.py b/mcp_server.py index 3ec14b63..cae26e21 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -112,36 +112,56 @@ def _derive_wealth_promise_strength(modules: Dict[str, Any]) -> Optional[Dict[st yogas_doshas = modules.get("yogas_doshas") if isinstance(modules, dict) else {} dhana = yogas_doshas.get("dhana_yogas") if isinstance(yogas_doshas, dict) else {} yogas = dhana.get("yogas") if isinstance(dhana, dict) else None - if not isinstance(yogas, list) or not yogas: + + has_dhana = False + has_lakshmi = False + dhana_level = "weak" + lakshmi_level = "weak" + + sources = set() + if isinstance(yogas, list) and yogas: + for row in yogas: + if not isinstance(row, dict): + continue + lvl = str(row.get("strength", "")).lower() + row_type = str(row.get("type", "")).lower() + if "lakshmi" in row_type: + sources.add("lakshmi") + has_lakshmi = True + if lvl == "strong" or (lvl == "moderate" and lakshmi_level == "weak"): + lakshmi_level = lvl + elif "dhana" in row_type: + sources.add("dhana") + has_dhana = True + if lvl == "strong" or (lvl == "moderate" and dhana_level == "weak"): + dhana_level = lvl + + if not has_dhana and not has_lakshmi: return None - strengths = [] - sources = set() - for row in yogas: - if not isinstance(row, dict): - continue - strengths.append(str(row.get("strength", "")).lower()) - row_type = str(row.get("type", "")).lower() - if "lakshmi" in row_type: - sources.add("lakshmi") - elif "dhana" in row_type: - sources.add("dhana") + supporting_sources = sorted(sources) - if any(level == "strong" for level in strengths): - level = "strong" - elif any(level == "moderate" for level in strengths): - level = "moderate" + if has_dhana and has_lakshmi: + primary_source = "dhana_lakshmi_hooks" + elif has_dhana: + primary_source = "dhana_yogas" else: - level = "weak" + primary_source = "lakshmi_hooks" + + if dhana_level == "strong" or lakshmi_level == "strong": + final_level = "strong" + elif dhana_level == "moderate" or lakshmi_level == "moderate": + final_level = "moderate" + else: + final_level = "weak" - supporting_sources = sorted(sources) or ["dhana"] - primary_source = "dhana_lakshmi_hooks" if len(supporting_sources) > 1 else "dhana_yogas" return { - "level": level, + "level": final_level, "primary_source": primary_source, "supporting_sources": supporting_sources, - "count": len(yogas), + "count": len(yogas) if isinstance(yogas, list) else 0, "source_diversity": len(supporting_sources), + "yogi_support": None, } @@ -405,7 +425,8 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An else: confidence_cap = "medium-low" event_judgement = _derive_event_judgement(route, present, missing) - if yogi_promise and event_judgement.get("dominant_label") and "yogi_active" not in event_judgement.get("secondary_context", []): + promise = present.get("wealth_promise_strength") or {} + if yogi_promise and "yogi" in promise.get("supporting_sources", []) and event_judgement.get("dominant_label") and "yogi_active" not in event_judgement.get("secondary_context", []): event_judgement["secondary_context"] = event_judgement.get("secondary_context", []) + ["yogi_active"] return { "question_type": route, diff --git a/tests/test_mcp_strict_workflow_finance.py b/tests/test_mcp_strict_workflow_finance.py index 4c7c08b4..f7b7e12c 100644 --- a/tests/test_mcp_strict_workflow_finance.py +++ b/tests/test_mcp_strict_workflow_finance.py @@ -3,7 +3,7 @@ from __future__ import annotations -from mcp_server import _collect_strict_evidence, _derive_event_judgement +from mcp_server import _collect_strict_evidence, _derive_event_judgement, _derive_wealth_promise_strength def test_finance_public_wealth_label_requires_at_least_moderate_window() -> None: @@ -174,6 +174,7 @@ def test_collect_strict_evidence_finance_derives_wealth_promise_from_dhana_yogas "count": 2, "source_diversity": 1, "supporting_sources": ["dhana"], + "yogi_support": None, } assert strict["event_judgement"]["score"] == 100 assert strict["event_judgement"]["verdict"] == "moderate_probability_window" @@ -214,9 +215,71 @@ def test_collect_strict_evidence_finance_combines_dhana_and_lakshmi_hooks() -> N "count": 2, "source_diversity": 2, "supporting_sources": ["dhana", "lakshmi"], + "yogi_support": None, } +def test_wealth_folding_dhana_does_not_self_infer_yogi_without_external_truth() -> None: + modules = { + "yogas_doshas": { + "dhana_yogas": { + "yogas": [{"type": "dhana", "strength": "strong"}] + } + }, + "chart": { + "ascendant": {"degree_raw": 133.0}, + "planets": { + "Sun": {"degree_raw": 20.0}, + "Moon": {"degree_raw": 20.0}, + "Venus": {"house": 11, "sign": "Gemini"}, + } + } + } + res = _derive_wealth_promise_strength(modules) + assert res["primary_source"] == "dhana_yogas" + assert res["source_diversity"] == 1 + assert res["supporting_sources"] == ["dhana"] + assert res["level"] == "strong" + assert res["yogi_support"] is None + + +def test_wealth_folding_dhana_lakshmi_does_not_self_infer_yogi_without_external_truth() -> None: + modules = { + "yogas_doshas": { + "dhana_yogas": { + "yogas": [{"type": "dhana", "strength": "moderate"}, {"type": "lakshmi", "strength": "moderate"}] + } + }, + "chart": { + "ascendant": {"degree_raw": 133.0}, + "planets": { + "Sun": {"degree_raw": 20.0}, + "Moon": {"degree_raw": 20.0}, + "Venus": {"house": 11, "sign": "Gemini"}, + } + } + } + res = _derive_wealth_promise_strength(modules) + assert res["primary_source"] == "dhana_lakshmi_hooks" + assert res["source_diversity"] == 2 + assert res["level"] == "moderate" + assert res["yogi_support"] is None + + +def test_wealth_folding_yogi_only_is_blocked_without_external_truth() -> None: + modules = { + "chart": { + "ascendant": {"degree_raw": 133.0}, + "planets": { + "Sun": {"degree_raw": 20.0}, + "Moon": {"degree_raw": 20.0}, + "Venus": {"house": 11, "sign": "Gemini"}, + } + } + } + assert _derive_wealth_promise_strength(modules) is None + + def test_collect_strict_evidence_finance_adds_yogi_hook_only_when_external_truth_is_present() -> None: result = { "modules": { @@ -253,12 +316,19 @@ def test_collect_strict_evidence_finance_adds_yogi_hook_only_when_external_truth } strict = _collect_strict_evidence("finance", result) + assert strict["present_evidence"]["yogi_promise"] == { + "planet": "Venus", + "house": 10, + "status": "入友(Friendly Sign)", + "source": "external_yogi_planet", + } assert strict["present_evidence"]["wealth_promise_strength"] == { "level": "moderate", "primary_source": "dhana_yogi_hooks", "count": 2, "source_diversity": 2, "supporting_sources": ["dhana", "yogi"], + "yogi_support": None, } assert "yogi_active" in strict["event_judgement"]["secondary_context"] @@ -306,5 +376,6 @@ def test_collect_strict_evidence_finance_does_not_promote_yogi_outside_kendra_tr "count": 1, "source_diversity": 1, "supporting_sources": ["dhana"], + "yogi_support": None, } assert "yogi_active" not in strict["event_judgement"]["secondary_context"]