Event scoring was measured at chance level on the 77 Rodden-AA holdout cases (BUG-1091). The new flow finds the dividing line nearest the reported time, describes how the flipped divisional charts read on either side, asks up to five self-assessment questions, and saves the user's own choice (provenance user_choice) with the domains to read in both versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbBXozbkyFNqbu8pVEtZ7W
58 lines
2.1 KiB
Python
58 lines
2.1 KiB
Python
"""Fixed rules that turn self-assessment answers into a chart choice.
|
|
|
|
No scoring. The rules were agreed with the product owner on 2026-11-06:
|
|
|
|
* When the reported time is within ``NEAR_LINE_MINUTES`` of the line, or is a
|
|
rounded time (minute divisible by 15), the reported time carries no real
|
|
preference, so the side with more answers wins. A tie keeps the reported side.
|
|
* Otherwise the reported side is kept unless at least ``OVERRIDE_VOTES`` answers
|
|
point to the other side and at most one answer points to the reported side.
|
|
* Domains whose answer points away from the chosen side are read in both versions.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Final, Literal
|
|
|
|
Side = Literal["before", "after"]
|
|
NEAR_LINE_MINUTES: Final = 5
|
|
OVERRIDE_VOTES: Final = 4
|
|
|
|
|
|
def reported_is_neutral(reported: datetime, line_at: datetime, *, reported_known: bool) -> bool:
|
|
if not reported_known:
|
|
return True
|
|
if reported.minute % 15 == 0:
|
|
return True
|
|
return abs((reported - line_at).total_seconds()) < NEAR_LINE_MINUTES * 60
|
|
|
|
|
|
def decide(
|
|
answers: dict[str, str],
|
|
*,
|
|
reported_side: Side,
|
|
neutral: bool,
|
|
) -> dict[str, Any]:
|
|
votes = {"before": 0, "after": 0, "unsure": 0}
|
|
for value in answers.values():
|
|
votes[value if value in votes else "unsure"] += 1
|
|
other: Side = "after" if reported_side == "before" else "before"
|
|
if neutral:
|
|
if votes[other] > votes[reported_side]:
|
|
chosen, rule = other, "majority"
|
|
elif votes[reported_side] > votes[other]:
|
|
chosen, rule = reported_side, "majority"
|
|
else:
|
|
chosen, rule = reported_side, "tie_keeps_reported"
|
|
elif votes[other] >= OVERRIDE_VOTES and votes[reported_side] <= 1:
|
|
chosen, rule = other, "strong_override"
|
|
else:
|
|
chosen, rule = reported_side, "reported_kept"
|
|
both_versions = sorted(domain for domain, value in answers.items() if value in ("before", "after") and value != chosen)
|
|
return {
|
|
"chosen_side": chosen,
|
|
"rule": rule,
|
|
"votes": votes,
|
|
"both_versions_domains": both_versions,
|
|
}
|