瑜伽角宫主和三方主按名字排序,不再随进程哈希变(BUG-1320,上游 0337b77f)
同一张盘在两个哈希种子下,宫主名单和瑜伽参与者顺序一致。pytest tests/test_yoga_lord_order_stable.py 1 通过。 Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
2748e43871
commit
4cdb997800
@@ -17750,3 +17750,18 @@
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。
|
||||
|
||||
## BUG-1320 | 瑜伽角宫主和三方主的顺序随进程哈希变化
|
||||
|
||||
- 状态:fixed-pending-verify
|
||||
- 首次发现 / 最近更新:2026-10-14 / 2026-10-14
|
||||
- 来源:issue #56。上游 `0337b77f`。任务书 T3。
|
||||
- 现象:同一张盘的角宫主、三方主列表用了集合去重。集合顺序会跟着进程哈希变,瑜伽参与者顺序不稳定。
|
||||
- 触发条件:规则表达式读取这两份宫主列表。
|
||||
- 根因:去重用了不保证顺序的集合。同步第六轮漏了上游把它们改成排序的那一处。
|
||||
- 修复:两处都改成先去重再按名字排序。
|
||||
- 验证:`tests/test_yoga_lord_order_stable.py` 1 通过。哈希种子 0 和 1 两个进程算出的参与者顺序相同。
|
||||
- 防复发:两个子进程的名单必须逐项相同,并且等于排好序的名单。
|
||||
- 相关记录:BUG-1319。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/upstream-sync7-20261014`,尚未合入 staging。
|
||||
|
||||
|
||||
@@ -1687,11 +1687,13 @@ class YogaEngine:
|
||||
# 辅助:获取角宫主星列表
|
||||
def _kendra_lords_list():
|
||||
ai = SIGNS.index(ctx.ascendant) if ctx.ascendant in SIGNS else 0
|
||||
return list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 4, 7, 10]]))
|
||||
# set 顺序随进程哈希变化;排序后同一张盘的角宫主参与者顺序才稳定。
|
||||
return sorted(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 4, 7, 10]]))
|
||||
# 辅助:获取三方宫主星列表
|
||||
def _trikona_lords_list():
|
||||
ai = SIGNS.index(ctx.ascendant) if ctx.ascendant in SIGNS else 0
|
||||
return list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 5, 9]]))
|
||||
# set 顺序随进程哈希变化;排序后同一张盘的三方主参与者顺序才稳定。
|
||||
return sorted(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 5, 9]]))
|
||||
|
||||
safe_globals = {
|
||||
# 安全内置函数:只开放规则表达式需要的纯函数,避免 custom 规则因 all/any/len/set 缺失而静默失效。
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""BUG-1320: kendra and trikona lord lists do not follow PYTHONHASHSEED.
|
||||
|
||||
Upstream 0337b77f. Two processes with different hash seeds must list the
|
||||
same yoga participants in the same order.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
_PROBE = r"""
|
||||
import json
|
||||
import sys
|
||||
sys.path.insert(0, "scripts")
|
||||
from yoga_engine import YogaContext, YogaEngine, detect_yogas
|
||||
|
||||
planets = {
|
||||
"Sun": {"sign": "Leo", "house": 5, "degree": 10},
|
||||
"Moon": {"sign": "Cancer", "house": 4, "degree": 5},
|
||||
"Mars": {"sign": "Aries", "house": 1, "degree": 8},
|
||||
"Mercury": {"sign": "Virgo", "house": 6, "degree": 12},
|
||||
"Jupiter": {"sign": "Sagittarius", "house": 9, "degree": 15},
|
||||
"Venus": {"sign": "Libra", "house": 7, "degree": 3},
|
||||
"Saturn": {"sign": "Capricorn", "house": 10, "degree": 20},
|
||||
"Rahu": {"sign": "Taurus", "house": 2, "degree": 9},
|
||||
"Ketu": {"sign": "Scorpio", "house": 8, "degree": 9},
|
||||
}
|
||||
rules = "references/yoga_rules.json"
|
||||
engine = YogaEngine(rules)
|
||||
ctx = YogaContext(planets, "Aries")
|
||||
namespace = engine._build_custom_exec_globals(ctx, {}, {})
|
||||
rows = detect_yogas(planets, "Aries", rules)
|
||||
print(json.dumps({
|
||||
"kendra": namespace["kendra_lords_list"](),
|
||||
"trikona": namespace["trikona_lords_list"](),
|
||||
"participants": [
|
||||
[row.get("rule_id"), str(row.get("combination"))]
|
||||
for row in rows
|
||||
],
|
||||
}, ensure_ascii=False))
|
||||
"""
|
||||
|
||||
|
||||
def _run(seed: str) -> dict:
|
||||
env = os.environ.copy()
|
||||
env["PYTHONHASHSEED"] = seed
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-c", _PROBE],
|
||||
cwd=ROOT,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
return json.loads(completed.stdout)
|
||||
|
||||
|
||||
def test_yoga_participants_keep_the_same_order_across_hash_seeds() -> None:
|
||||
first = _run("0")
|
||||
second = _run("1")
|
||||
assert first == second
|
||||
assert first["kendra"] == sorted(first["kendra"])
|
||||
assert first["trikona"] == sorted(first["trikona"])
|
||||
assert first["kendra"] == ["Mars", "Moon", "Saturn", "Venus"]
|
||||
assert first["trikona"] == ["Jupiter", "Mars", "Sun"]
|
||||
Reference in New Issue
Block a user