fix(rectification): emit finer dated probes on refresh only (BUG-664/665)
Independent Staging Quality Gate / validate (push) Successful in 10m11s
Independent Staging Quality Gate / publish (push) Successful in 7m26s

After six dated answers, refresh can use 30-day same-year windows plus pratyantar and D9/D10 Narayana. First-round probe rules stay at 45 days.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-13 18:19:46 +08:00
co-authored by Cursor
parent 530f260f76
commit ab1ade59b7
7 changed files with 403 additions and 43 deletions
+5
View File
@@ -1,5 +1,10 @@
# 印度占星 Skill 更新日志
## 2026-09-13 — 六道带年月题问完后,刷新还能再问更细的换运题
生时校正里,带年月选择题问完后,会按还剩下的候选再出一批带年月题。这一批现在能用更短的同年换运间隔(30 天),也会看第三级换运和 D9/D10 上升的分盘大运。问不出时仍一条一条问定向补事,不会停在「没有拿到下一个问题」。首轮出题规则不变。Skill 版本不变(10.0.25)。
## 2026-09-13 — 定向补事改成逐条点选;范围提示说剩余候选;卡下可再答参考题
生时校正里,带年月选择题问完、引擎也没有新题时,会一条一条问还能切开剩余候选的线:先问有没有(有 / 没有 / 记不清),说「有」再问大概哪年几月。说「没有」只记下这条线近年无事,不计分。提示写「现在还剩某段时间里 N 个候选」,不再写「能把两端钟点分开」。四条线都问完才给出目前范围。卡片下面可以再点「再答两道参考题微调排序」,不点不出。Skill 10.0.25。
+32
View File
@@ -10324,3 +10324,35 @@
- 相关记录:BUG-629、BUG-651、BUG-661
- 复发自:BUG-629(性格题降权后没有可选入口)
- 修复版本:待发布
## BUG-664 | 刷新阶段同年换运间隔可收到 30 天,首轮仍是 45 天
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:`scripts/rectification/event_probes.py` `_boundary_windows` / `_union_boundary_dates` / `discriminating_event_probes`
- 用户现象:六道带年月题答完后,刷新经常立刻 `no_new_probes`,即使剩余候选的同年换运只差三十多天。
- 触发条件:训练门已开、带年月池空、`refresh_probes=true`、剩余候选上存在 30–44 天的同年边界。
- 根因:生产 `MIN_BOUNDARY_DAYS=45` 对首轮和刷新共用。离线 20 例测量里,只把刷新阈值降到 30 平均多 1.0 个独立划分、头名不降;首轮保持 45。
- 修复:新增 `REFRESH_MIN_BOUNDARY_DAYS=30`。仅 `request.refresh_probes is True` 时传给 `_union_boundary_dates`;首轮调用签名与阈值不变。不改 `EXISTENCE_NEARBY_YEARS`、R1/R2、`SCORE_DELTA`
- 验证:`tests/test_rectification_refresh_r3_r4.py`40 天同年窗首轮不出、刷新出 `dasha_boundary`。既有 `tests/test_rectification_event_probes.py``tests/test_candidate_discriminator_contract.py` 首轮断言不放宽。
- 防复发:默认 `MIN_BOUNDARY_DAYS` 必须仍是 45;不得把研究脚本接到 API;刷新仍走 distinguish 合同。
- 相关记录:BUG-653、BUG-657、BUG-665
- 复发自:BUG-653(刷新已按剩余候选出题,但边界阈值与首轮相同)
- 修复版本:待发布
## BUG-665 | 刷新阶段并入 pratyantar 与 D9/D10 上升 Narayana 边界
- 状态:resolved
- 首次发现:2026-09-13
- 最近更新:2026-09-13
- 影响面:`scripts/rectification/event_probes.py` `_vim_start_dates` / `_union_boundary_dates`
- 用户现象:六道带年月题答完后,本命上升相同、D9/D10 或第三级换运已不同时,刷新仍无新的带年月题。
- 触发条件:`refresh_probes=true`,剩余候选 Vimshottari pratyantar 或 D9/D10 上升 Narayana 有可分辨边界。
- 根因:首轮并集只用 Vimshottari MD/AD 与本命上升 Narayana。`_vim_start_dates(..., include_pratyantar=)` 已存在但刷新默认仍为 false。离线测量 R4 单独 +1.084 独立划分,头名不降。
- 修复:仅刷新阶段 `include_pratyantar=True`,并对剩余候选 D9/D10 上升再算 Narayana 边界并入并集。首轮不传这些参数,`_vim_start_dates` 调用保持原签名以免旧 mock 失败。本命上升 Narayana 与 MD/AD 不变。
- 验证:`tests/test_rectification_refresh_r3_r4.py`pratyantar 与 D9 上升不同时刷新出新年月、首轮不出。研究脚本 `scripts/research/probe_supply_after_six.py` 仍离线。
- 防复发:不得对首轮打开 pratyantar 或分盘 Narayana;不得全局改 `MIN_BOUNDARY_DAYS`。刷新 0 题时仍走定向补事(BUG-654 / BUG-661),不得报「没有拿到下一个问题」。
- 相关记录:BUG-653、BUG-654、BUG-661、BUG-664
- 复发自:BUG-653(刷新只复用首轮边界层)
- 修复版本:待发布
@@ -0,0 +1,36 @@
# 进度 · 刷新阶段放宽换运边界 R3 + R42026-09-13
## 范围
- 分支:`codex/rectification-refresh-r3-r4-20260913`(基于 `origin/staging` @ `530f260f`
- 任务单:`docs/tasks/TASK-rectification-refresh-r3-r4-20260913.md`
- 研究结论:`docs/research/probe_supply_after_six_2026_09_13.md`;只实现 R3 + R4
- BUG-664:刷新 `MIN_BOUNDARY_DAYS` 45→30
- BUG-665:刷新并入 pratyantar 与 D9/D10 上升 Narayana
- 不实现 R1/R2;不改 `EXISTENCE_NEARBY_YEARS``SCORE_DELTA`、确认门、性格题计分(BUG-629)
- 研究脚本 `scripts/research/probe_supply_after_six.py` 保持离线
- Skill 不升版:用户路径仍是「刷新带年月题 → 定向补事 → 目前范围」,只是刷新可能多几道同类题;10.0.25 口径已覆盖
## 完成
- T1 `REFRESH_MIN_BOUNDARY_DAYS=30`;仅 `refresh_probes is True` 时传入 `_union_boundary_dates`。首轮仍调用 `_union_boundary_dates(reps, birth_date=..., lo=lo, hi=hi)``MIN_BOUNDARY_DAYS` 仍为 45。
- T2 仅刷新:`include_pratyantar=True`(首轮不把该关键字传给 `_vim_start_dates`,以免旧 mock 失败);D9/D10 上升 Narayana 与本命上升并集。
- T3 `tests/test_rectification_refresh_r3_r4.py`40 天同年窗、pratyantar、D9 上升不同;年份用 2019,避开搬家/婚恋年龄下限与邻近年封锁。
- T4 BUG-664/665、CHANGELOG、本文件、任务板、手测 0b/0c 补「刷新有题则先出带年月题」。
## 验收(本机,未部署)
- `tests/test_rectification_refresh_r3_r4.py`4 passed
- `tests/test_rectification_refresh_r3_r4.py` + `tests/test_rectification_event_probes.py` + `tests/test_candidate_discriminator_contract.py`51 passed
- `tests/test_rectification_*.py`171 passed / 0 failed
- `run_quality_gate.py --profile quick`738 passed / 1 skipped / 1 failed
- 失败项:`test_block_scan_seven_events_finishes_within_fifteen_seconds` 壁钟 46.2s > 15s
- 与本单无关(未改 `block_scan`);任务板 `TASK-rectification-capability-fix-20260907.md` 已记为 P2 间歇红
- 未做登录态真机 0b(无本地会话)
## 未做
- 未把研究脚本接到 API
- 未改首轮出题、未实现 R1/R2
- 未升 Skill 10.0.26
- 未做登录态真机 0b(无本地会话)
+1 -1
View File
@@ -178,7 +178,7 @@
| `TASK-rectification-after-pool-empty-decision-20260913.md` | — | 决策单草案:六道带年月题问完、引擎刷新无新题后,定向补事是开放式口述题,体感死板。给出 A 逐条点选 / B 放宽引擎出题(先离线量收益)/ C 参考题可选入口 / D 文案 四个方案,建议 A+D(+C) | 已拍板 2026-09-13A+D+C 进 `TASK-rectification-targeted-collect-cards-20260913.md`B 进研究单 | — |
| `TASK-rectification-targeted-collect-cards-20260913.md` | `PROGRESS-rectification-targeted-collect-cards-20260913.md` | 定向补事改逐条点选卡(有/没有/记不清 → 有则口述年月;没有不计分只防复问;全没有出卡)+ 文案说清剩余候选与线 + 卡下「再答两道参考题」入口(BUG-661663Skill 10.0.25 | 待验收 | `codex/rectification-targeted-collect-cards-20260913` |
| `TASK-rectification-probe-supply-research-20260913.md` | — | 研究单:六题后引擎在剩余候选上再出带年月题的四种放宽规则,20 例公开 AA 数据离线量收益,有收益才立实现单 | 已合入(离线测量,不改线上) | `b063c668` |
| `TASK-rectification-refresh-r3-r4-20260913.md` | | 刷新阶段 R3MIN_BOUNDARY_DAYS 45→30+ R4pratyantar 与 D9/D10 上升 Narayana);首轮出题不变。即使放宽仍有 0 题例子,定向补事另线保留(BUG-664/665 | 待领取 | |
| `TASK-rectification-refresh-r3-r4-20260913.md` | `PROGRESS-rectification-refresh-r3-r4-20260913.md` | 刷新阶段 R3MIN_BOUNDARY_DAYS 45→30+ R4pratyantar 与 D9/D10 上升 Narayana);首轮出题不变。即使放宽仍有 0 题例子,定向补事另线保留(BUG-664/665 | 待验收 | `codex/rectification-refresh-r3-r4-20260913` |
## 命名与归档
@@ -28,6 +28,7 @@
期望:
- 第六题答完不得直接出卡;先出现新的带年月题(家人 / 财务 / 迁居等换运边界),或一张定向补事点选卡(先问有没有)
- 若剩余候选只在 D9/D10 或更短的同年换运上有差别,第六题后仍应先看到带年月选择题,而不是立刻跳到「结过婚或订过婚吗?」;仍无题才走定向补事
- 刷新后若引擎给出新的带年月题,下一张卡必须真正出现;不得只是决策选中了探针、落库被拒后停在「没有拿到下一个问题」
- 答一件家人事后应重算并回到带年月题;答「没有了 / 就这些」后才出卡
- 卡片标题为「目前范围 …(对照了 N 件经历)」,卡下有「还能再收窄:如果记得 …」
@@ -41,7 +42,7 @@
期望:
- 第六题答完必须看到定向补事点选卡(例如「结过婚或订过婚吗?」);旁白写剩余范围和候选个数,不得写「能把两端钟点分开」;题干种类必须能写入焦点表,不得静默失败
- 第六题答完必须看到下一问:优先新的带年月选择题;引擎没有新题时才是定向补事点选卡(例如「结过婚或订过婚吗?」);旁白写剩余范围和候选个数,不得写「能把两端钟点分开」;题干种类必须能写入焦点表,不得静默失败
- 引擎若没有新的带年月题,界面走定向补事或已有载体;同一候选集、同一已答数下引擎刷新只调用一次,不得反复打约 20 秒的刷新
- 引擎若有新题,下一问必须出现;不得「GET 已有探针、界面没有下一问」
- 刷新不得把候选集改空或换成另一套 `candidate_set_id`
+84 -41
View File
@@ -66,6 +66,7 @@ MAX_PROBES_PER_DOMAIN = 3
# MAX_PROBES is the published cap after a global information_gain sort.
MAX_BOUNDARY_CANDIDATES_PER_DOMAIN = 8
MIN_BOUNDARY_DAYS = 45
REFRESH_MIN_BOUNDARY_DAYS = 30
MAX_QUALITY_DISTINGUISH_PROBES = 2
QUALITY_DISTINGUISH_EVENT_KINDS = frozenset({
"education_start",
@@ -525,15 +526,21 @@ def _as_start_date(value: date | datetime | int) -> date | None:
return None
def _boundary_windows(left: Sequence[date | datetime | int], right: Sequence[date | datetime | int]) -> list[date]:
def _boundary_windows(
left: Sequence[date | datetime | int],
right: Sequence[date | datetime | int],
*,
min_days: int | None = None,
) -> list[date]:
windows: list[date] = []
seen: set[tuple[int, int]] = set()
threshold = MIN_BOUNDARY_DAYS if min_days is None else min_days
for raw_left, raw_right in zip(left, right):
one = _as_start_date(raw_left)
two = _as_start_date(raw_right)
if one is None or two is None:
continue
if one.year == two.year and abs((one - two).days) < MIN_BOUNDARY_DAYS:
if one.year == two.year and abs((one - two).days) < threshold:
continue
for item in (one, two):
key = (item.year, item.month)
@@ -605,54 +612,77 @@ def _union_boundary_dates(
birth_date: str,
lo: int,
hi: int,
min_boundary_days: int | None = None,
include_pratyantar: bool = False,
varga_narayana: bool = False,
) -> list[date]:
threshold = MIN_BOUNDARY_DAYS if min_boundary_days is None else min_boundary_days
vim_cache: dict[tuple[Any, ...], list[date]] = {}
narayana_cache: dict[tuple[Any, ...], list[date] | None] = {}
dates_by_key: dict[tuple[int, int], date] = {}
for left, right in _representative_pairs(reps):
left_moon = float(left["planet_longitudes"]["Moon"])
right_moon = float(right["planet_longitudes"]["Moon"])
left_vim_key = _vim_cache_key(birth_date, left_moon, lo, hi)
right_vim_key = _vim_cache_key(birth_date, right_moon, lo, hi)
left_vim_key = (*_vim_cache_key(birth_date, left_moon, lo, hi), include_pratyantar)
right_vim_key = (*_vim_cache_key(birth_date, right_moon, lo, hi), include_pratyantar)
if left_vim_key not in vim_cache:
vim_cache[left_vim_key] = _vim_start_dates(birth_date, left_moon, lo, hi)
vim_cache[left_vim_key] = (
_vim_start_dates(
birth_date, left_moon, lo, hi, include_pratyantar=True,
)
if include_pratyantar
else _vim_start_dates(birth_date, left_moon, lo, hi)
)
if right_vim_key not in vim_cache:
vim_cache[right_vim_key] = _vim_start_dates(birth_date, right_moon, lo, hi)
windows = list(_boundary_windows(vim_cache[left_vim_key], vim_cache[right_vim_key]))
left_nara_key = _narayana_cache_key(
int(left["ascendant_index"]),
left["planet_longitudes"],
birth_date,
lo,
hi,
)
right_nara_key = _narayana_cache_key(
int(right["ascendant_index"]),
right["planet_longitudes"],
birth_date,
lo,
hi,
)
if left_nara_key not in narayana_cache:
narayana_cache[left_nara_key] = _narayana_start_dates(
int(left["ascendant_index"]),
left["planet_longitudes"],
birth_date,
lo,
hi,
vim_cache[right_vim_key] = (
_vim_start_dates(
birth_date, right_moon, lo, hi, include_pratyantar=True,
)
if include_pratyantar
else _vim_start_dates(birth_date, right_moon, lo, hi)
)
if right_nara_key not in narayana_cache:
narayana_cache[right_nara_key] = _narayana_start_dates(
int(right["ascendant_index"]),
right["planet_longitudes"],
birth_date,
lo,
hi,
)
left_narayana = narayana_cache[left_nara_key]
right_narayana = narayana_cache[right_nara_key]
if left_narayana is not None and right_narayana is not None:
windows.extend(_boundary_windows(left_narayana, right_narayana))
window_kw = {} if threshold == MIN_BOUNDARY_DAYS else {"min_days": threshold}
windows = list(_boundary_windows(
vim_cache[left_vim_key],
vim_cache[right_vim_key],
**window_kw,
))
layers: list[str | None] = [None]
if varga_narayana:
layers.extend(["d9", "d10"])
for layer in layers:
if layer is None:
left_asc = int(left["ascendant_index"])
right_asc = int(right["ascendant_index"])
else:
left_raw = _layer_value(left, layer)
right_raw = _layer_value(right, layer)
if not isinstance(left_raw, int) or not isinstance(right_raw, int):
continue
left_asc = left_raw
right_asc = right_raw
left_nara_key = (*_narayana_cache_key(
left_asc, left["planet_longitudes"], birth_date, lo, hi,
), layer)
right_nara_key = (*_narayana_cache_key(
right_asc, right["planet_longitudes"], birth_date, lo, hi,
), layer)
if left_nara_key not in narayana_cache:
narayana_cache[left_nara_key] = _narayana_start_dates(
left_asc, left["planet_longitudes"], birth_date, lo, hi,
)
if right_nara_key not in narayana_cache:
narayana_cache[right_nara_key] = _narayana_start_dates(
right_asc, right["planet_longitudes"], birth_date, lo, hi,
)
left_narayana = narayana_cache[left_nara_key]
right_narayana = narayana_cache[right_nara_key]
if left_narayana is not None and right_narayana is not None:
windows.extend(_boundary_windows(
left_narayana,
right_narayana,
**window_kw,
))
for item in windows:
dates_by_key.setdefault((item.year, item.month), item)
return sorted(dates_by_key.values(), key=lambda item: (item.year, item.month))
@@ -1567,7 +1597,20 @@ def _discriminating_event_probe_lists(
d1_differs="d1" in remaining_layers or bool(scan.get("d1_candidates_differ")),
)
lo, hi = birth_year + 5, min(now.year, birth_year + 80)
boundary_dates = _union_boundary_dates(reps, birth_date=birth_date, lo=lo, hi=hi) if domains else []
union_kw: dict[str, Any] = {}
if refresh:
union_kw = {
"min_boundary_days": REFRESH_MIN_BOUNDARY_DAYS,
"include_pratyantar": True,
"varga_narayana": True,
}
boundary_dates = _union_boundary_dates(
reps,
birth_date=birth_date,
lo=lo,
hi=hi,
**union_kw,
) if domains else []
probes: list[dict[str, Any]] = []
for domain in domains:
if domain not in DOMAIN_CATALOG:
+243
View File
@@ -0,0 +1,243 @@
from __future__ import annotations
import unittest
from datetime import date, datetime
from unittest.mock import patch
from scripts.rectification.event_probes import (
MIN_BOUNDARY_DAYS,
REFRESH_MIN_BOUNDARY_DAYS,
_boundary_windows,
discriminating_event_probes,
)
from scripts.rectification.refinement_packet import window_scan
PLANETS = {
"Sun": 12.0,
"Moon": 100.0,
"Mars": 40.0,
"Mercury": 20.0,
"Jupiter": 80.0,
"Venus": 50.0,
"Saturn": 200.0,
"Rahu": 310.0,
"Ketu": 130.0,
}
# Relocation age_lo=18 → 2015; asked relocation.2015 blocks 20142016.
# Relationship age_lo=21 → 2018; asked relationship.2023 / event 2021 leave 2019 open.
ADULT_YEAR = 2019
TIMES = ["04:48", "04:53", "04:59", "05:06", "05:07"]
ASKED = [
"career.2020",
"career.2023",
"education.2016",
"education.2020",
"relationship.2023",
"relocation.2015",
]
def _varga(asc: int) -> dict:
return {
"Ascendant": {"sign_idx": asc},
**{name: {"sign_idx": 1} for name in PLANETS},
}
def _context(time: str, *, moon: float, d4_asc: int, d9_asc: int, d10_asc: int) -> dict:
hour, minute = (int(part) for part in time.split(":"))
planets = {**PLANETS, "Moon": moon}
return {
"candidate_at": datetime(1997, 8, 8, hour, minute),
"chart": {
"ascendant": {"lon": 10.0, "sign": "Aries"},
"planets": {
name: {"house": 4 if name == "Moon" else 10, "lon": lon}
for name, lon in planets.items()
},
},
"planet_longitudes": dict(planets),
"ascendant_index": 0,
"varga_charts": {
"D4": _varga(d4_asc),
"D9": _varga(d9_asc),
"D10": _varga(d10_asc),
"D5": _varga(1),
"D24": _varga(1),
"D12": _varga(1),
"D7": _varga(1),
"D3": _varga(1),
},
"arudha_padas": {},
"feature": {
"time": time,
"ascendant_sign_index": 0,
"varga_ascendants": {
"D4": d4_asc, "D9": d9_asc, "D10": d10_asc, "D5": 1, "D24": 1, "D12": 1,
},
},
}
def _gate_events() -> list[dict]:
return [
{
"id": "00000000-0000-4000-8000-000000000011",
"domain": "education",
"event_kind": "education_start",
"summary": "入学",
"date": "2014-09-01",
"precision": "month",
},
{
"id": "00000000-0000-4000-8000-000000000012",
"domain": "education",
"event_kind": "education_completion",
"summary": "毕业",
"date": "2017-06-01",
"precision": "month",
},
{
"id": "00000000-0000-4000-8000-000000000013",
"domain": "career",
"event_kind": "career_entry",
"summary": "入职",
"date": "2018-07-01",
"precision": "month",
},
{
"id": "00000000-0000-4000-8000-000000000014",
"domain": "relationship",
"event_kind": "relationship_start",
"summary": "相识",
"date": "2021-08-01",
"precision": "month",
},
]
def _built(*, d4_left: int = 0, d4_right: int = 1, d9_left: int = 1, d9_right: int = 1, d10_left: int = 1, d10_right: int = 1) -> dict:
early = dict(moon=100.0, d4_asc=d4_left, d9_asc=d9_left, d10_asc=d10_left)
late = dict(moon=101.0, d4_asc=d4_right, d9_asc=d9_right, d10_asc=d10_right)
return {
"static_contexts": [
_context("04:48", **early),
_context("04:53", **early),
_context("04:59", **early),
_context("05:06", **late),
_context("05:07", **late),
]
}
def _score_months(target_year: int, months: set[int], rule: str = "vim_ad_domain_lord"):
def fake_score(context: dict, *, birth_date: str, domain: str, year: int, month: int | None = None) -> dict:
del birth_date, domain
early = str(context.get("feature", {}).get("time") or "")[:5] < "05:00"
if year == target_year and month in months:
return {"rule_ids": [rule] if early else ["no_domain_activation"]}
return {"rule_ids": ["no_domain_activation"]}
return fake_score
def _probes(*, refresh: bool, built: dict, vim, narayana, score):
from scripts.rectification import event_probes as probes_mod
request = {
"birth_date": "1997-08-08",
"events": _gate_events(),
"asked_probe_keys": ASKED,
"refresh_probes": refresh,
}
with (
patch.object(probes_mod, "_vim_start_dates", side_effect=vim),
patch.object(probes_mod, "_narayana_start_dates", side_effect=narayana),
patch.object(probes_mod, "_score_year", side_effect=score),
):
return discriminating_event_probes(
request,
built,
scan=window_scan(built),
candidate_times=TIMES,
representative_time="04:53",
today=date(2026, 9, 13),
)
class RefreshR3R4Test(unittest.TestCase):
def test_default_boundary_threshold_stays_45(self) -> None:
self.assertEqual(MIN_BOUNDARY_DAYS, 45)
self.assertEqual(REFRESH_MIN_BOUNDARY_DAYS, 30)
left = [date(ADULT_YEAR, 3, 1)]
right = [date(ADULT_YEAR, 4, 10)]
self.assertEqual(_boundary_windows(left, right), [])
self.assertEqual(
[item.month for item in _boundary_windows(left, right, min_days=30)],
[3, 4],
)
def test_first_round_drops_a_40_day_same_year_window(self) -> None:
def fake_vim(_birth: str, moon: float, _lo: int, _hi: int, **_kwargs: object) -> list[date]:
return [date(ADULT_YEAR, 3, 1)] if moon <= 100.0 else [date(ADULT_YEAR, 4, 10)]
def fake_narayana(_asc: int, _planets: dict, _birth: str, _lo: int, _hi: int) -> list[date]:
return []
score = _score_months(ADULT_YEAR, {3, 4})
built = _built()
first = _probes(refresh=False, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
refresh = _probes(refresh=True, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
self.assertFalse(any(item.get("year") == ADULT_YEAR for item in first))
self.assertTrue(any(
item.get("year") == ADULT_YEAR and item.get("source") == "dasha_boundary"
for item in refresh
), [item.get("semantic_key") for item in refresh])
def test_refresh_adds_pratyantar_windows_first_round_does_not(self) -> None:
def fake_vim(_birth: str, moon: float, _lo: int, _hi: int, *, include_pratyantar: bool = False) -> list[date]:
md = [date(2011, 1, 1)]
if not include_pratyantar:
return md
extra = date(ADULT_YEAR, 2, 1) if moon <= 100.0 else date(ADULT_YEAR, 4, 1)
return [*md, extra]
def fake_narayana(_asc: int, _planets: dict, _birth: str, _lo: int, _hi: int) -> list[date]:
return []
score = _score_months(ADULT_YEAR, {2, 4})
built = _built()
first = _probes(refresh=False, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
refresh = _probes(refresh=True, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
self.assertFalse(any(item.get("year") == ADULT_YEAR for item in first))
self.assertTrue(
any(item.get("year") == ADULT_YEAR for item in refresh),
[item.get("semantic_key") for item in refresh],
)
def test_refresh_adds_d9_narayana_when_natal_lagna_matches(self) -> None:
def fake_vim(_birth: str, _moon: float, _lo: int, _hi: int, **_kwargs: object) -> list[date]:
return [date(2011, 1, 1)]
def fake_narayana(asc: int, _planets: dict, _birth: str, _lo: int, _hi: int) -> list[date]:
if asc == 1:
return [date(ADULT_YEAR, 6, 1)]
if asc == 2:
return [date(ADULT_YEAR, 7, 21)]
return [date(2011, 1, 1)]
score = _score_months(ADULT_YEAR, {6, 7}, rule="narayana_ad_domain_lord")
built = _built(d4_left=0, d4_right=0, d9_left=1, d9_right=2)
first = _probes(refresh=False, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
refresh = _probes(refresh=True, built=built, vim=fake_vim, narayana=fake_narayana, score=score)
self.assertFalse(any(item.get("year") == ADULT_YEAR for item in first))
self.assertTrue(
any(item.get("year") == ADULT_YEAR for item in refresh),
[item.get("semantic_key") for item in refresh],
)
if __name__ == "__main__":
unittest.main()