fix(rectification): fold D24 into education refine and score family D3

Window scan now drives d5_refine when D24 changes, family events use D3, and pada/Hora/Ghati are display-only. New cases bind Skill 10.0.7 without unique-minute confirmation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-20 11:04:55 +08:00
parent 9a5cf67209
commit 70efb56926
35 changed files with 894 additions and 72 deletions
+1
View File
@@ -52,6 +52,7 @@ _AUDIT_LABELS = {
"d2-hora": ("D2 财帛分盘", "本轮已对照财帛主题。"),
"d4-chaturthamsha": ("D4 迁移分盘", "本轮已对照居所或迁移。"),
"d5-panchamsha": ("D5 成就分盘", "本轮已对照学业或被委以责任的变化。"),
"d3-drekkana": ("D3 兄弟分盘", "本轮已对照兄弟姐妹主题。"),
"d7-saptamsha": ("D7 子女分盘", "本轮已对照子女或伴侣细节。"),
"d9-navamsa": ("D9 婚姻分盘", "本轮已对照关系主题,未给类型标签。"),
"d10-dashamsa": ("D10 事业分盘", "本轮已对照事业主题,未给类型标签。"),
+31 -28
View File
@@ -121,25 +121,38 @@ _LAYER_LABEL = {
"d5": "D5",
"d7": "D7",
"d12": "D12",
"d24": "D24",
"pada": "Nakshatra pada",
"hora": "Hora Lagna",
"ghati": "Ghati Lagna",
}
def _scan_layer_value(feature: dict[str, Any], layer: str) -> int | None:
vargas = feature.get("varga_ascendants") if isinstance(feature.get("varga_ascendants"), dict) else {}
raw = {
"d1": feature.get("ascendant_sign_index"),
"d9": vargas.get("D9"),
"d10": vargas.get("D10"),
"d4": vargas.get("D4"),
"d5": vargas.get("D5"),
"d7": vargas.get("D7"),
"d12": vargas.get("D12"),
"d24": vargas.get("D24"),
"pada": feature.get("pada_index"),
"hora": feature.get("hora_sign_index"),
"ghati": feature.get("ghati_sign_index"),
}.get(layer)
return raw if isinstance(raw, int) else None
def window_scan(built: dict[str, Any]) -> dict[str, Any]:
"""D1/D9/D10/D4/D5/D7/D12 diversity plus change minutes. Indices only; never sign names."""
"""D1/D9/D10/D4/D5/D7/D12/D24 plus display-only pada/Hora/Ghati. Indices only; never sign names."""
counts: dict[str, set[int]] = {layer: set() for layer in _LAYER_LABEL}
transitions: list[dict[str, Any]] = []
previous: dict[str, int | None] | None = None
for feature in _features(built):
vargas = feature.get("varga_ascendants") if isinstance(feature.get("varga_ascendants"), dict) else {}
current = {
"d1": feature.get("ascendant_sign_index") if isinstance(feature.get("ascendant_sign_index"), int) else None,
"d9": vargas.get("D9") if isinstance(vargas.get("D9"), int) else None,
"d10": vargas.get("D10") if isinstance(vargas.get("D10"), int) else None,
"d4": vargas.get("D4") if isinstance(vargas.get("D4"), int) else None,
"d5": vargas.get("D5") if isinstance(vargas.get("D5"), int) else None,
"d7": vargas.get("D7") if isinstance(vargas.get("D7"), int) else None,
"d12": vargas.get("D12") if isinstance(vargas.get("D12"), int) else None,
}
current = {layer: _scan_layer_value(feature, layer) for layer in _LAYER_LABEL}
for layer, bucket in counts.items():
value = current[layer]
if isinstance(value, int):
@@ -156,26 +169,16 @@ def window_scan(built: dict[str, Any]) -> dict[str, Any]:
"user_meaning": f"{label}{time} 发生变化",
})
previous = current
return {
payload: dict[str, Any] = {
"scanned": True,
"confirmation_allowed": False,
"unique_minute_claim": False,
"d1_lagna_count": len(counts["d1"]),
"d9_lagna_count": len(counts["d9"]),
"d10_lagna_count": len(counts["d10"]),
"d4_lagna_count": len(counts["d4"]),
"d5_lagna_count": len(counts["d5"]),
"d7_lagna_count": len(counts["d7"]),
"d12_lagna_count": len(counts["d12"]),
"d1_candidates_differ": len(counts["d1"]) > 1,
"d9_candidates_differ": len(counts["d9"]) > 1,
"d10_candidates_differ": len(counts["d10"]) > 1,
"d4_candidates_differ": len(counts["d4"]) > 1,
"d5_candidates_differ": len(counts["d5"]) > 1,
"d7_candidates_differ": len(counts["d7"]) > 1,
"d12_candidates_differ": len(counts["d12"]) > 1,
"transitions": transitions,
}
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
return payload
def event_dasha_ledger(
@@ -376,9 +379,9 @@ def precision_stage(scan: dict[str, Any], event_count: int) -> dict[str, Any]:
elif scan.get("d4_candidates_differ"):
current = "d4_refine"
meaning = "事业盘已较稳,居所盘仍会换升。可再补一件记得时间的搬家或住处变化。"
elif scan.get("d5_candidates_differ"):
elif scan.get("d5_candidates_differ") or scan.get("d24_candidates_differ"):
current = "d5_refine"
meaning = "居所盘已较稳,成就盘仍会换升。可再补一件记得时间的学业、考试或被委以责任的变化;不要贴类型标签。"
meaning = "居所盘已较稳,成就盘或学业盘仍会换升。可再补一件记得时间的学业、考试或被委以责任的变化;不要贴类型标签。"
else:
current = "ready_to_adopt"
meaning = "核心分盘已不再换升。可以采用代表性时间看盘,也可以再补主题经历。"
+2 -2
View File
@@ -12,7 +12,7 @@ from scripts.active_rectification_event_engine import compute_candidate_static_c
from scripts.active_rectification_events import CandidateScoreRow
from scripts.rectification.contracts import LifeEvent, RectificationRequest, is_scoreable_event
ALGORITHM_VERSION = "rectification-v5-matrix-scoring-4"
ALGORITHM_VERSION = "rectification-v5-matrix-scoring-5"
INPUT_CONTRACT_VERSION = "rectification-calculation-spec-v4"
PRECISION_WEIGHTS = {
"day": 1.0,
@@ -184,7 +184,7 @@ def public_technique_layers(domain: str, rule_ids: Sequence[str]) -> list[str]:
if domain == "career":
layers.update({"d1-rashi", "d10-dashamsa"})
elif domain == "family":
layers.update({"d1-rashi", "d12-dwadashamsha", "d7-saptamsha"})
layers.update({"d1-rashi", "d12-dwadashamsha", "d7-saptamsha", "d3-drekkana"})
elif domain == "education":
layers.update({"d1-rashi", "d24-chaturvimshamsha", "d5-panchamsha"})
elif domain == "relocation":