feat(factor): 决议O①卡片死亡回写聚合化——全死才埋(反查卡下全部因子,部分死=卡留活跃池只影响展示;death_reason=全灭:触发因子x入墓园:死因;修D5任一死埋整卡在一卡多因子语境错位) [vps]
This commit is contained in:
@@ -18,6 +18,7 @@ from sanguo_factor.version_registry import (
|
||||
save_registry,
|
||||
transition,
|
||||
upsert_factor,
|
||||
writeback_card_death,
|
||||
)
|
||||
|
||||
|
||||
@@ -221,8 +222,9 @@ def test_graveyard_transition_writes_back_card(tmp_path, monkeypatch):
|
||||
|
||||
def test_writeback_skips_non_card_hypothesis(tmp_path):
|
||||
import sanguo_factor.version_registry as vr
|
||||
assert vr.writeback_card_death(None, {"hypothesis": "H-2026-09-22-05",
|
||||
"name": "f"}, "x") is False
|
||||
assert vr.writeback_card_death(None, {"factors": {}},
|
||||
{"hypothesis": "H-2026-09-22-05",
|
||||
"name": "f"}, "x") is False
|
||||
|
||||
|
||||
def _insert_card(db):
|
||||
@@ -246,7 +248,8 @@ def test_writeback_card_not_in_db_leaves_trace(tmp_path, capsys):
|
||||
_insert_card(db) # 库存在有表,但回写目标 hyp-missing 不在
|
||||
events = str(tmp_path / "ev.jsonl")
|
||||
rc = vr.writeback_card_death(
|
||||
db, {"hypothesis": "hyp-missing", "name": "f_ghost"}, "死因",
|
||||
db, {"factors": {}}, {"hypothesis": "hyp-missing",
|
||||
"name": "f_ghost"}, "死因",
|
||||
events_path=events)
|
||||
assert rc is False
|
||||
err = capsys.readouterr().err
|
||||
@@ -267,7 +270,8 @@ def test_writeback_relative_pipeline_db_absolutized(tmp_path, monkeypatch, capsy
|
||||
hyp = _insert_card(db)
|
||||
events = str(tmp_path / "ev.jsonl")
|
||||
rc = vr.writeback_card_death(
|
||||
db, {"hypothesis": hyp, "name": "f_rel"}, "死因", events_path=events)
|
||||
db, {"factors": {}}, {"hypothesis": hyp, "name": "f_rel"}, "死因",
|
||||
events_path=events)
|
||||
assert rc is True
|
||||
err = capsys.readouterr().err
|
||||
assert "相对路径" in err
|
||||
@@ -284,7 +288,8 @@ def test_writeback_missing_db_skipped_not_created(tmp_path, capsys):
|
||||
db = str(tmp_path / "sub" / "pipeline.db") # 连同父目录都不存在
|
||||
events = str(tmp_path / "ev.jsonl")
|
||||
rc = vr.writeback_card_death(
|
||||
db, {"hypothesis": "hyp-404", "name": "f_lost"}, "死因",
|
||||
db, {"factors": {}}, {"hypothesis": "hyp-404",
|
||||
"name": "f_lost"}, "死因",
|
||||
events_path=events)
|
||||
assert rc is False
|
||||
err = capsys.readouterr().err
|
||||
@@ -332,3 +337,55 @@ def test_upsert_factor_description_built_once_not_overwritten(reg):
|
||||
# 不传 description=条目无该键(存量因子形态)
|
||||
upsert_factor(reg, "f2", "H-2")
|
||||
assert "description" not in reg["factors"]["f2"]
|
||||
|
||||
|
||||
class TestWritebackAggregation:
|
||||
"""决议O:卡片死亡回写=全死才埋(任一因子死不再埋整卡)."""
|
||||
|
||||
def _card_db(self, tmp_path):
|
||||
from sanguo_portfolio import pipeline_store as ps
|
||||
db = str(tmp_path / "pipeline.db")
|
||||
ps.insert_hypothesis(db, {
|
||||
"title": "高管增持后跑赢", "logic": "x",
|
||||
"expected_sign": "positive", "falsifiable": "y"})
|
||||
with __import__("sqlite3").connect(db) as conn:
|
||||
conn.execute(
|
||||
"UPDATE hypotheses SET id='hyp-agg1' WHERE title='高管增持后跑赢'")
|
||||
return db
|
||||
|
||||
def test_partial_death_keeps_card_alive(self, tmp_path, capsys):
|
||||
db = self._card_db(tmp_path)
|
||||
reg = {"factors": {
|
||||
"fa_a": {"name": "fa_a", "hypothesis": "hyp-agg1",
|
||||
"status": "graveyard"}, # 死的那个(触发者)
|
||||
"fa_b": {"name": "fa_b", "hypothesis": "hyp-agg1",
|
||||
"status": "incubating"},}} # 活着 → 不埋卡
|
||||
ok = writeback_card_death(db, reg, reg["factors"]["fa_a"], "IC 两窗反号")
|
||||
assert ok is False
|
||||
out = capsys.readouterr().err
|
||||
assert "部分因子存活" in out and "fa_b" in out
|
||||
from sanguo_portfolio import pipeline_store as ps
|
||||
card = [c for c in ps.list_hypotheses(db) if c["id"] == "hyp-agg1"][0]
|
||||
assert card["state"] == "queued" # 卡留活跃池
|
||||
|
||||
def test_all_dead_buries_card_with_new_reason(self, tmp_path):
|
||||
db = self._card_db(tmp_path)
|
||||
reg = {"factors": {
|
||||
"fa_a": {"name": "fa_a", "hypothesis": "hyp-agg1",
|
||||
"status": "graveyard"},
|
||||
"fa_b": {"name": "fa_b", "hypothesis": "hyp-agg1",
|
||||
"status": "graveyard"},}} # 全灭 → 埋
|
||||
ok = writeback_card_death(db, reg, reg["factors"]["fa_a"], "IC 两窗反号")
|
||||
assert ok is True
|
||||
from sanguo_portfolio import pipeline_store as ps
|
||||
card = [c for c in ps.list_hypotheses(db) if c["id"] == "hyp-agg1"][0]
|
||||
assert card["state"] == "graveyard"
|
||||
assert card["deathReason"] == "全灭:触发因子 fa_a 入墓园: IC 两窗反号"
|
||||
|
||||
def test_zero_sibling_factor_buries(self, tmp_path):
|
||||
"""卡下无其他因子(一卡一因子旧语境)→该因子死即全灭,照埋."""
|
||||
db = self._card_db(tmp_path)
|
||||
reg = {"factors": {
|
||||
"fa_a": {"name": "fa_a", "hypothesis": "hyp-agg1",
|
||||
"status": "graveyard"},}}
|
||||
assert writeback_card_death(db, reg, reg["factors"]["fa_a"], "x") is True
|
||||
|
||||
Reference in New Issue
Block a user