feat(rectification): expose candidate result reports
Independent Staging Quality Gate / validate (push) Successful in 16m46s
Independent Staging Quality Gate / publish (push) Successful in 19m37s

This commit is contained in:
Jesse_Chen
2026-08-31 05:51:30 +08:00
parent 6cbf1f22e2
commit e8bd3a5208
6 changed files with 366 additions and 5 deletions
+66
View File
@@ -680,6 +680,72 @@ class RectificationV5ServicesTest(unittest.TestCase):
self.assertEqual(API_COMMAND_MAP[command], endpoint)
self.assertIn(endpoint, TECHNIQUE_EXAMPLE_ENDPOINTS)
def test_zero_evidence_returns_declared_window_report(self):
body = request(start_time="23:58", end_time="00:02")
body["events"] = []
normalized = normalize_rectification_request(body, today=date(2026, 7, 28))
result = score_candidates(normalized)
report = result["rectification_report"]
self.assertEqual(report["candidate_range"]["start_time"], "23:58")
self.assertEqual(report["candidate_range"]["end_time"], "00:02")
self.assertEqual(report["candidate_range"]["width_minutes"], 5)
self.assertIsNone(report["representative_time"])
self.assertEqual(report["evidence"], [])
self.assertFalse(report["candidate_range"]["representative_is_unique"])
self.assertIn("do_not_apply_as_birth_time_truth", result["next_step_codes"])
def test_report_exposes_evidence_excluded_candidates_and_diagnostics_consistently(self):
body = request()
body["events"] = [
event(1, "education", "education_start"),
event(2, "career", "promotion"),
]
normalized = normalize_rectification_request(body, today=date(2026, 7, 28))
built = {
"candidate_times": ["05:13", "05:14", "05:15"],
"matrix": {
item["id"]: {
"05:13": {"points": 4, "rule_ids": ["D10:test"], "technique_layers": ["D10"]},
"05:14": {"points": 4, "rule_ids": ["D9:test"], "technique_layers": ["D9"]},
"05:15": {"points": -1, "rule_ids": ["D1:test"], "technique_layers": ["D1"]},
}
for item in normalized["events"]
},
"date_sensitivity": [],
"missing_layers": [],
"static_contexts": [
{"feature": {"time": "05:13", "ascendant_sign_index": 1}},
{"feature": {"time": "05:14", "ascendant_sign_index": 2}},
{"feature": {"time": "05:15", "ascendant_sign_index": 3}},
],
}
diagnostic_values = {
"primary_cluster_retention_rate": 1,
"leave_one_event_out_retention_rate": 1,
"leave_one_domain_out_retention_rate": 1,
"date_sensitivity_retention_rate": 1,
"neighbor_support_minutes": 1,
"primary_secondary_margin_percent": 30,
}
with patch("scripts.rectification.api_service.build_event_contribution_matrix", return_value=built), patch(
"scripts.rectification.api_service.build_candidate_feature_snapshot", return_value={}
), patch("scripts.rectification.api_service.run_diagnostics", return_value=diagnostic_values):
scored = score_candidates(normalized)
diagnostic_result = diagnostics(normalized)
report = scored["rectification_report"]
self.assertEqual(report["candidate_range"]["start_time"], "05:13")
self.assertEqual(report["candidate_range"]["end_time"], "05:14")
self.assertEqual(report["representative_time"], "05:13")
self.assertEqual([row["status"] for row in report["evidence"]], ["supporting", "supporting"])
self.assertEqual(report["evidence"][0]["methods"], ["D10"])
self.assertGreaterEqual(len(report["excluded_candidates"]), 1)
self.assertEqual(scored["candidate_summary"]["stability"]["label"], scored["overall_confidence"])
self.assertEqual(diagnostic_result["rectification_report"], report)
self.assertEqual(diagnostic_result["next_step_codes"], scored["next_step_codes"])
def test_http_handler_enforces_subject_and_event_kind_boundaries(self):
handler = object.__new__(JyotishAPIHandler)
retained = handler._rectification_v5_request(request(domain="family", event_kind="family_event"))