Revise wearer entry and yield annotation protocol

This commit is contained in:
Xin Wang
2026-09-15 01:25:39 +08:00
parent 026d89978f
commit 71dfd8808a
12 changed files with 478 additions and 248 deletions

View File

@@ -59,8 +59,9 @@ class FloorEventReviewTests(unittest.TestCase):
"dense_start_seconds": 1.2,
"event_time": 1.2,
"candidate_source": "WEARER_ONSET",
"task": "wearer_entry",
"ai_verdict": "POSITIVE",
"ai_event_type": "EOT_RESPONSE",
"ai_event_type": "WEARER_ENTRY",
"ai_subtype": None,
"speaker_timeline": [
{
@@ -129,7 +130,7 @@ class FloorEventReviewTests(unittest.TestCase):
resumed = ReviewStore(output, candidates)
self.assertEqual(
resumed.reviews[candidates[0].candidate_id]["review"]["human_event_type"],
"EOT_RESPONSE",
"WEARER_ENTRY",
)
self.assertFalse(resumed.reviews[candidates[0].candidate_id]["review"]["relabeled"])
self.assertEqual(resumed.reviews[candidates[0].candidate_id]["review"]["reviewer"], "")
@@ -162,7 +163,7 @@ class FloorEventReviewTests(unittest.TestCase):
{
"uid": "Session_4/wearer_onset/00000338",
"candidate_source": "WEARER_ONSET",
"task": "eot_response",
"task": "wearer_entry",
},
]
path.write_text(
@@ -183,18 +184,83 @@ class FloorEventReviewTests(unittest.TestCase):
"human_action": "relabel",
"human_verdict": "HARD_NEGATIVE",
"human_event_type": None,
"human_subtype": "EOT_WEARER_BACKCHANNEL",
"human_subtype": "ENTRY_WEARER_BACKCHANNEL",
"exclude_reason": None,
"boundary_correction_seconds": None,
"confidence": "medium",
"reviewer": "r2",
"notes": "brief acknowledgement",
})
self.assertEqual(record["ai_event_type"], "EOT_RESPONSE")
self.assertEqual(record["review"]["human_subtype"], "EOT_WEARER_BACKCHANNEL")
self.assertEqual(record["ai_event_type"], "WEARER_ENTRY")
self.assertEqual(record["review"]["human_subtype"], "ENTRY_WEARER_BACKCHANNEL")
self.assertTrue(record["review"]["relabeled"])
def test_eot_review_selects_only_a_supplied_opportunity_anchor(self) -> None:
def test_removed_verdicts_cannot_be_accepted_or_reintroduced(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
_, events = self._fixture(root)
candidates = load_candidates([events])
candidate = candidates[0]
store = ReviewStore(root / "reviews.jsonl", candidates)
base = {
"human_event_type": None, "human_subtype": None,
"selected_opportunity_uid": None, "exclude_reason": None,
"boundary_correction_seconds": None, "confidence": "medium",
"notes": "legacy fallback",
}
for verdict in ("UNSURE", "NOT_APPLICABLE"):
with self.subTest(action="relabel", verdict=verdict), self.assertRaisesRegex(
ValueError, "Invalid human_verdict"
):
store.save(candidate.candidate_id, {
**base, "human_action": "relabel", "human_verdict": verdict,
})
candidate.payload["ai_verdict"] = verdict
with self.subTest(action="accept", verdict=verdict), self.assertRaisesRegex(
ValueError, "Accept requires an AI pre-label"
):
store.save(candidate.candidate_id, {
**base, "human_action": "accept", "human_verdict": None,
})
candidate.payload["ai_verdict"] = "POSITIVE"
def test_relabel_subtype_must_match_candidate_family(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
_, events = self._fixture(root)
candidates = load_candidates([events])
store = ReviewStore(root / "reviews.jsonl", candidates)
with self.assertRaisesRegex(ValueError, "ENTRY candidate requires ENTRY_"):
store.save(candidates[0].candidate_id, {
"human_action": "relabel", "human_verdict": "HARD_NEGATIVE",
"human_event_type": None, "human_subtype": "INT_OTHER_BACKCHANNEL",
"selected_opportunity_uid": None, "exclude_reason": None,
"boundary_correction_seconds": None, "confidence": "medium",
"notes": "wrong family",
})
def test_normal_completion_can_be_excluded_as_out_of_scope(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
_, events = self._fixture(root)
candidates = load_candidates([events])
store = ReviewStore(root / "reviews.jsonl", candidates)
record = store.save(candidates[0].candidate_id, {
"human_action": "exclude", "human_verdict": None,
"human_event_type": None, "human_subtype": None,
"selected_opportunity_uid": None,
"exclude_reason": "OUT_OF_SCOPE_NORMAL_COMPLETION",
"boundary_correction_seconds": None, "confidence": "high",
"notes": "wearer completed; laughter did not take over",
})
self.assertEqual(record["review"]["human_verdict"], "EXCLUDE")
self.assertEqual(
record["review"]["exclude_reason"], "OUT_OF_SCOPE_NORMAL_COMPLETION"
)
def test_entry_review_may_select_only_a_supplied_opportunity_anchor(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
_, events = self._fixture(root)
@@ -208,7 +274,7 @@ class FloorEventReviewTests(unittest.TestCase):
store = ReviewStore(root / "reviews.jsonl", candidates)
base = {
"human_action": "relabel", "human_verdict": "POSITIVE",
"human_event_type": "EOT_RESPONSE", "human_subtype": None,
"human_event_type": "WEARER_ENTRY", "human_subtype": None,
"exclude_reason": None, "boundary_correction_seconds": None,
"confidence": "high", "notes": "clear response",
}
@@ -219,11 +285,15 @@ class FloorEventReviewTests(unittest.TestCase):
self.assertEqual(
record["review"]["valid_prediction_interval_seconds"], [1.0, 1.2]
)
with self.assertRaisesRegex(ValueError, "requires one supplied"):
with self.assertRaisesRegex(ValueError, "only a supplied"):
store.save(
candidate.candidate_id,
{**base, "selected_opportunity_uid": "invented"},
)
optional = store.save(
candidate.candidate_id, {**base, "selected_opportunity_uid": None}
)
self.assertIsNone(optional["review"]["selected_opportunity_uid"])
def test_byte_ranges(self) -> None:
self.assertEqual(parse_byte_range("bytes=2-5", 10), (2, 5))