feat(product): present consult and rectification in local skill form
Default ayanamsa to Raman with true_pushya support, attach governed Raman packets, restore Path C questionnaires and eight-method verification copy, and keep unique-minute confirmation blocked. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -56,8 +56,8 @@ _AUDIT_LABELS = {
|
||||
"d5-panchamsha": ("D5 成就分盘", "本轮已对照学业或被委以责任的变化。"),
|
||||
"d3-drekkana": ("D3 兄弟分盘", "本轮已对照兄弟姐妹主题。"),
|
||||
"d7-saptamsha": ("D7 子女分盘", "本轮已对照子女或伴侣细节。"),
|
||||
"d9-navamsa": ("D9 婚姻分盘", "本轮已对照关系主题,未给类型标签。"),
|
||||
"d10-dashamsa": ("D10 事业分盘", "本轮已对照事业主题,未给类型标签。"),
|
||||
"d9-navamsa": ("D9 婚姻分盘", "本轮已对照关系主题,可用 D9 上升类型表作校时方法,不是命运承诺。"),
|
||||
"d10-dashamsa": ("D10 事业分盘", "本轮已对照事业主题,可用 D10 上升类型表作校时方法,不是命运承诺。"),
|
||||
"d11-labhamsha": ("D11 收益分盘", "本轮已对照收益主题。"),
|
||||
"d12-dwadashamsha": ("D12 父母分盘", "本轮已对照家人主题。"),
|
||||
"d24-chaturvimshamsha": ("D24 教育分盘", "本轮已对照学业主题。"),
|
||||
@@ -596,6 +596,7 @@ def build_decision_receipt(
|
||||
receipt.update({
|
||||
"window_scan": packet["window_scan"],
|
||||
"event_dasha_ledger": packet["event_dasha_ledger"],
|
||||
"event_fit_rate": packet["event_fit_rate"],
|
||||
"dasha_agreement": packet["dasha_agreement"],
|
||||
"lagna_contrast": packet["lagna_contrast"],
|
||||
"nakshatra_boundary": packet["nakshatra_boundary"],
|
||||
|
||||
@@ -61,7 +61,7 @@ def build_horary_observation(request: dict[str, Any]) -> dict[str, Any]:
|
||||
"lat": float(request["lat"]),
|
||||
"lon": float(request["lon"]),
|
||||
"tz": float(request["tz"]),
|
||||
"ayanamsa": "lahiri",
|
||||
"ayanamsa": "raman",
|
||||
"node_mode": "mean",
|
||||
})
|
||||
asc = chart.get("ascendant") or {}
|
||||
|
||||
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from ayanamsa_utils import apply_ayanamsa, current_ayanamsa_name
|
||||
from ayanamsa_utils import DEFAULT_AYANAMSA_NAME, apply_ayanamsa, current_ayanamsa_name
|
||||
from kp_system import KP_LORDS, get_kp_lords
|
||||
from scripts.domain_calculation_service import swiss_ephemeris_lock
|
||||
|
||||
@@ -84,4 +84,4 @@ def observe_kp_cusps(jd: float | None, lat: float | None, lon: float | None) ->
|
||||
except (TypeError, ValueError, OverflowError, OSError, RuntimeError, ArithmeticError):
|
||||
return dict(_BLOCKED)
|
||||
finally:
|
||||
apply_ayanamsa(previous or "lahiri", swe)
|
||||
apply_ayanamsa(previous or DEFAULT_AYANAMSA_NAME, swe)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Server-owned P0/P1 refinement packet for birth-time rectification.
|
||||
|
||||
Produces candidate-narrowing structure only. Never grants a unique minute,
|
||||
never emits D9/D10 type labels, and never copies raw scores into public copy.
|
||||
Produces candidate-narrowing structure only. Never grants a unique minute
|
||||
and never copies raw scores into public copy. D9/D10 sign names are method
|
||||
contrast for the skill verification report, not unique-minute proof.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -76,6 +77,18 @@ def _features(built: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
return rows
|
||||
|
||||
|
||||
def _sign_names(indices: set[int]) -> list[str]:
|
||||
names: list[str] = []
|
||||
for idx in sorted(indices):
|
||||
if isinstance(idx, int) and 0 <= idx <= 11:
|
||||
names.append(SIGNS_CN[SIGNS[idx]])
|
||||
return names
|
||||
|
||||
|
||||
def _has_gochara(rule_ids: Sequence[str]) -> bool:
|
||||
return any("controlled_transit" in str(item) or str(item).startswith("gochara") for item in rule_ids)
|
||||
|
||||
|
||||
def match_level(rule_ids: Sequence[str]) -> str:
|
||||
ids = [str(item) for item in rule_ids]
|
||||
if not ids or ids == ["no_domain_activation"]:
|
||||
@@ -92,6 +105,8 @@ def match_level(rule_ids: Sequence[str]) -> str:
|
||||
return "medium"
|
||||
if any(item.startswith("vim_") or item.startswith("narayana_") for item in ids):
|
||||
return "weak"
|
||||
if _has_gochara(ids):
|
||||
return "medium"
|
||||
return "none"
|
||||
|
||||
|
||||
@@ -101,6 +116,8 @@ def _tracks(rule_ids: Sequence[str]) -> list[str]:
|
||||
tracks.append("vimshottari")
|
||||
if any(str(item).startswith("narayana_") for item in rule_ids):
|
||||
tracks.append("narayana")
|
||||
if _has_gochara(rule_ids):
|
||||
tracks.append("gochara")
|
||||
return tracks
|
||||
|
||||
|
||||
@@ -196,6 +213,8 @@ def window_scan(built: dict[str, Any]) -> dict[str, Any]:
|
||||
for layer in _LAYER_LABEL:
|
||||
payload[f"{layer}_lagna_count" if layer.startswith("d") else f"{layer}_count"] = len(counts[layer])
|
||||
payload[f"{layer}_candidates_differ"] = len(counts[layer]) > 1
|
||||
payload["d9_sign_names"] = _sign_names(counts["d9"])
|
||||
payload["d10_sign_names"] = _sign_names(counts["d10"])
|
||||
return payload
|
||||
|
||||
|
||||
@@ -219,18 +238,58 @@ def event_dasha_ledger(
|
||||
summary = str(event.get("summary") or "").strip() or "这条经历"
|
||||
tracks = _tracks(rule_ids)
|
||||
track_text = "、".join(
|
||||
"主限" if track == "vimshottari" else "分盘大运" for track in tracks
|
||||
"主限" if track == "vimshottari" else "分盘大运" if track == "narayana" else "受控行运"
|
||||
for track in tracks
|
||||
) or "现有大运层"
|
||||
gochara_hit = _has_gochara(rule_ids)
|
||||
rows.append({
|
||||
"summary": summary[:80],
|
||||
"match": level,
|
||||
"match_label": MATCH_LABELS[level],
|
||||
"tracks": tracks,
|
||||
"user_meaning": f"{summary[:40]}:{MATCH_LABELS[level]}({track_text})",
|
||||
"gochara": "activated" if gochara_hit else "not_seen",
|
||||
"user_meaning": (
|
||||
f"{summary[:40]}:{MATCH_LABELS[level]}({track_text}"
|
||||
f"{';Gochara 激活相关宫' if gochara_hit else ';Gochara 未见对应'})"
|
||||
),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def event_fit_rate(rows: Sequence[dict[str, Any]]) -> dict[str, Any]:
|
||||
total = len(rows)
|
||||
matched = sum(1 for row in rows if row.get("match") in {"strong", "medium"})
|
||||
if total == 0:
|
||||
return {
|
||||
"matched": 0,
|
||||
"total": 0,
|
||||
"percent": None,
|
||||
"band": "insufficient",
|
||||
"label": "事件不足,无法计算吻合率",
|
||||
"unique_minute_claim": False,
|
||||
"user_meaning": "事件–Dasha–Gochara 表还没有可评分行。这不是唯一分钟确认。",
|
||||
}
|
||||
percent = round(100 * matched / total)
|
||||
band = "high" if percent >= 80 else "medium" if percent >= 60 else "low"
|
||||
label = (
|
||||
"高度吻合(事件吻合率 ≥80%)" if band == "high"
|
||||
else "中度吻合(事件吻合率 60–80%)" if band == "medium"
|
||||
else "低度吻合(事件吻合率 <60%)"
|
||||
)
|
||||
return {
|
||||
"matched": matched,
|
||||
"total": total,
|
||||
"percent": percent,
|
||||
"band": band,
|
||||
"label": label,
|
||||
"unique_minute_claim": False,
|
||||
"user_meaning": (
|
||||
f"当前窗 {matched}/{total} 件已确认事件与 Dasha/Gochara 吻合,{label}。"
|
||||
"这是相对拟合,不是已确认唯一出生分钟。"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def dasha_agreement(built: dict[str, Any], candidate_times: Sequence[str]) -> dict[str, Any]:
|
||||
times = [str(item)[:5] for item in candidate_times if isinstance(item, str) and len(str(item)) >= 5]
|
||||
if not times:
|
||||
@@ -324,7 +383,7 @@ def lagna_contrast(built: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"user_meaning": (
|
||||
f"窗口里出现两段本命上升:{left['start']}-{left['end']} 为{left['lagna']},"
|
||||
f"{right['start']}-{right['end']} 为{right['lagna']}。"
|
||||
"只比较宫主结构,不给性格或类型标签。"
|
||||
"可并列 D9/D10 类型表作校时方法,不是命运承诺,也不能确认唯一分钟。"
|
||||
),
|
||||
"unique_minute_claim": False,
|
||||
}
|
||||
@@ -399,7 +458,7 @@ def precision_stage(scan: dict[str, Any], event_count: int) -> dict[str, Any]:
|
||||
meaning = "事业盘已较稳,居所盘仍会换升。可再补一件记得时间的搬家或住处变化。"
|
||||
elif scan.get("d5_candidates_differ") or scan.get("d24_candidates_differ"):
|
||||
current = "d5_refine"
|
||||
meaning = "居所盘已较稳,成就盘或学业盘仍会换升。可再补一件记得时间的学业、考试或被委以责任的变化;不要贴类型标签。"
|
||||
meaning = "居所盘已较稳,成就盘或学业盘仍会换升。可再补一件记得时间的学业、考试或被委以责任的变化。"
|
||||
else:
|
||||
current = "ready_to_adopt"
|
||||
meaning = "核心分盘已不再换升。可以采用代表性时间看盘,也可以再补主题经历。"
|
||||
@@ -441,10 +500,12 @@ def build_refinement_packet(
|
||||
candidate_times: Sequence[str],
|
||||
) -> dict[str, Any]:
|
||||
scan = window_scan(built)
|
||||
ledger = event_dasha_ledger(request, built, representative_time)
|
||||
agreement = dasha_agreement(built, candidate_times)
|
||||
return {
|
||||
"window_scan": scan,
|
||||
"event_dasha_ledger": event_dasha_ledger(request, built, representative_time),
|
||||
"event_dasha_ledger": ledger,
|
||||
"event_fit_rate": event_fit_rate(ledger),
|
||||
"dasha_agreement": agreement,
|
||||
"lagna_contrast": lagna_contrast(built),
|
||||
"nakshatra_boundary": nakshatra_boundary(built, representative_time),
|
||||
|
||||
@@ -328,7 +328,7 @@ def calculation_spec(request: RectificationRequest) -> dict[str, Any]:
|
||||
"latitude": json_number(request["lat"]),
|
||||
"longitude": json_number(request["lon"]),
|
||||
"timezoneOffsetHours": json_number(request["tz"]),
|
||||
"ayanamsa": "lahiri", "nodeMode": "mean", "minuteStep": 1,
|
||||
"ayanamsa": "raman", "nodeMode": "mean", "minuteStep": 1,
|
||||
}
|
||||
for source, target in (
|
||||
("birth_time_source", "birthTimeSource"),
|
||||
|
||||
Reference in New Issue
Block a user