fix(web): count only training events for discrimination and split user-stop from validated range

Three collected events with a reserved holdout were stalling because the discriminator door counted holdout. Public selection_allowed still had snapshot fallbacks, and health only proved the image SHA.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-26 19:36:04 +08:00
co-authored by Cursor
parent a0ce55f066
commit b1173f7245
37 changed files with 1362 additions and 234 deletions
+54 -2
View File
@@ -8,9 +8,11 @@ from scripts.rectification.candidate_contrast import (
MIN_DISCRIMINATOR_DOMAINS,
MIN_DISCRIMINATOR_EVENTS,
SIGNATURE_LAYERS,
discriminator_gate_open,
distinguish_contract_errors,
feature_signature,
select_signature_representatives,
training_scoreable_stats,
)
from scripts.rectification.event_probes import (
candidate_contrast_opportunities,
@@ -90,6 +92,7 @@ def _gate_events() -> list[dict]:
{"id": "e1", "domain": "education", "event_kind": "education_start", "date": "2014-09-01", "precision": "month"},
{"id": "e2", "domain": "education", "event_kind": "education_completion", "date": "2017-06-01", "precision": "month"},
{"id": "e3", "domain": "career", "event_kind": "career_entry", "date": "2018-07-01", "precision": "month"},
{"id": "e4", "domain": "relationship", "event_kind": "relationship_start", "date": "2021-08-01", "precision": "month"},
]
@@ -190,6 +193,45 @@ class DiscriminatorContractTest(unittest.TestCase):
self.assertGreaterEqual(MIN_DISCRIMINATOR_EVENTS, 3)
self.assertGreaterEqual(MIN_DISCRIMINATOR_DOMAINS, 2)
def test_training_gate_needs_four_events_when_one_is_holdout(self) -> None:
two = _gate_events()[:2]
three = _gate_events()[:3]
four = _gate_events()
self.assertFalse(discriminator_gate_open(two))
self.assertFalse(discriminator_gate_open(three))
two_count, _, _ = training_scoreable_stats(two)
three_count, three_domains, _ = training_scoreable_stats(three)
four_count, four_domains, _ = training_scoreable_stats(four)
self.assertLess(two_count, MIN_DISCRIMINATOR_EVENTS)
self.assertLess(three_count, MIN_DISCRIMINATOR_EVENTS)
self.assertGreaterEqual(four_count, MIN_DISCRIMINATOR_EVENTS)
self.assertGreaterEqual(four_domains, MIN_DISCRIMINATOR_DOMAINS)
built = {
"static_contexts": [
_context("05:13", d4_asc=0, sun_house=4, sun_varga_sign=3),
_context("05:40", d4_asc=1, sun_house=10, sun_varga_sign=9),
]
}
three_probes = discriminating_event_probes(
{"birth_date": "1997-08-08", "events": three},
built,
scan=window_scan(built),
candidate_times=["05:13", "05:40"],
representative_time="05:13",
today=date(2026, 8, 22),
)
four_probes = discriminating_event_probes(
{"birth_date": "1997-08-08", "events": four},
built,
scan=window_scan(built),
candidate_times=["05:13", "05:40"],
representative_time="05:13",
today=date(2026, 8, 22),
)
self.assertEqual(three_probes, [])
self.assertTrue(four_probes)
self.assertGreater(three_domains, 0)
def test_signature_clusters_are_not_three_adjacent_minutes(self) -> None:
rows = [
{"time": "05:13", "score": 20},
@@ -286,6 +328,10 @@ class DiscriminatorContractTest(unittest.TestCase):
"05:00": {"points": 100, "rule_ids": []},
"05:20": {"points": 0, "rule_ids": []},
},
"e4": {
"05:00": {"points": 4, "rule_ids": []},
"05:20": {"points": 1, "rule_ids": []},
},
},
"missing_layers": [],
}
@@ -311,9 +357,15 @@ class DiscriminatorContractTest(unittest.TestCase):
}
rows = score_from_matrix(request, built)
by_time = {row["time"]: row for row in rows}
self.assertEqual(by_time["05:00"]["score"], 20)
training_ids = {event["id"] for event in events} - holdout
expected = sum(
built["matrix"][event_id]["05:00"]["points"]
for event_id in training_ids
if event_id in built["matrix"]
)
self.assertEqual(by_time["05:00"]["score"], expected)
self.assertFalse(any(item["event_id"] == holdout_id for item in by_time["05:00"]["evidence"]))
self.assertIn(holdout_id, built["matrix"])
self.assertIn(holdout_id, {event["id"] for event in events})
probes = discriminating_event_probes(
_request(events=events),
@@ -48,6 +48,15 @@ def _request(*, extra_events=()):
"date_start": "2015-06-01",
"date_end": "2015-06-01",
},
{
"id": "00000000-0000-4000-8000-000000000010",
"domain": "career",
"summary": "职责变化",
"event_kind": "career_change",
"precision": "day",
"date_start": "2019-04-01",
"date_end": "2019-04-01",
},
*extra_events,
]
return {"events": events}
+8
View File
@@ -102,6 +102,14 @@ def _gate_events() -> list[dict]:
"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",
},
]
@@ -79,7 +79,8 @@ def request_events() -> dict:
{"id": "00000000-0000-4000-8000-000000000001", "domain": "career", "summary": "入职", "event_kind": "career_entry", "precision": "day", "date_start": "2016-09-15", "date_end": "2016-09-15"},
{"id": "00000000-0000-4000-8000-000000000002", "domain": "relationship", "summary": "开始一段关系", "event_kind": "relationship_start", "precision": "day", "date_start": "2018-03-01", "date_end": "2018-03-01"},
{"id": "00000000-0000-4000-8000-000000000003", "domain": "education", "summary": "毕业", "event_kind": "education_completion", "precision": "day", "date_start": "2015-06-01", "date_end": "2015-06-01"},
]
{"id": "00000000-0000-4000-8000-000000000004", "domain": "family", "summary": "家人变化", "event_kind": "family_event", "precision": "day", "date_start": "2020-01-01", "date_end": "2020-01-01"},
],
}
@@ -263,7 +264,7 @@ class RefinementPacketTest(unittest.TestCase):
self.assertNotIn("points", str(packet["event_dasha_ledger"]))
self.assertEqual(packet["lagna_contrast"]["intervals"][0]["lagna"], "金牛座")
self.assertTrue(packet["nakshatra_boundary"]["near_boundary"])
self.assertEqual(packet["oos_blind_prompts"][0]["domain"], "family")
self.assertEqual(packet["oos_blind_prompts"][0]["domain"], "finance")
self.assertFalse(packet["oos_blind_prompts"][0]["used_for_scoring"])
self.assertFalse(packet["confirmation_allowed"])
encoded = str(packet["window_scan"])
+4 -2
View File
@@ -417,10 +417,11 @@ class RectificationV5ServicesTest(unittest.TestCase):
event(1, "education", "education_start"),
event(2, "career", "promotion", precision="month"),
event(3, "finance", "finance_gain"),
event(5, "family", "family_event"),
event(4, "other", "other", precision="year"),
]
normalized = normalize_rectification_request(body, today=date(2026, 7, 28))
scored_ids = [item["id"] for item in normalized["events"][:3]]
scored_ids = [item["id"] for item in normalized["events"] if item["domain"] != "other"]
built = {
"candidate_times": ["05:13", "05:14", "05:15"],
"matrix": {
@@ -495,7 +496,7 @@ class RectificationV5ServicesTest(unittest.TestCase):
self.assertEqual(receipt["representative_time"], "05:13")
entries = first["execution_ledger"]
background = next(item for item in entries if item.get("event_id") == normalized["events"][3]["id"])
background = next(item for item in entries if item.get("event_id") == normalized["events"][4]["id"])
self.assertEqual(background["status"], "retained_not_scored")
executed = next(item for item in entries if item.get("event_id") == normalized["events"][0]["id"])
self.assertEqual(executed["technique_layers"], ["vim_md_domain_house"])
@@ -592,6 +593,7 @@ class RectificationV5ServicesTest(unittest.TestCase):
event(1, "career", "career_entry"),
event(2, "career", "promotion"),
event(3, "education", "education_start"),
event(4, "family", "family_event"),
]
normalized = normalize_rectification_request(body, today=date(2026, 7, 28))
built = {