feat(factor): 决议O①卡片死亡回写聚合化——全死才埋(反查卡下全部因子,部分死=卡留活跃池只影响展示;death_reason=全灭:触发因子x入墓园:死因;修D5任一死埋整卡在一卡多因子语境错位) [vps]

This commit is contained in:
2026-10-10 22:24:59 +08:00
parent 6aa0632595
commit c9b448a494
3 changed files with 83 additions and 15 deletions
+62 -5
View File
@@ -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