fix(rectification): score family events and allow appearance follow-up
Dated family evidence now moves candidates via D12 and kin houses, career receipts expose both D1-10 and D10, and appearance/marks may be asked as auxiliary first-house scores. New cases bind Skill 10.0.4. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,9 +27,12 @@ EVENT_KINDS: dict[str, frozenset[str]] = {
|
||||
"health": frozenset({"self_health_event", "pressure_period"}),
|
||||
"health_pressure": frozenset({"self_health_event", "pressure_period"}), # v1 domain compatibility
|
||||
"family": frozenset({"family_event"}),
|
||||
"appearance": frozenset({"appearance_note"}),
|
||||
"marks": frozenset({"birthmark_or_scar"}),
|
||||
"other": frozenset({"other"}),
|
||||
}
|
||||
BACKGROUND_EVENT_KINDS = frozenset({"family_event", "other"})
|
||||
BACKGROUND_EVENT_KINDS = frozenset({"other"})
|
||||
AUXILIARY_EVENT_KINDS = frozenset({"appearance_note", "birthmark_or_scar"})
|
||||
SCOREABLE_EVENT_KINDS: dict[str, frozenset[str]] = {
|
||||
domain: frozenset(kind for kind in kinds if kind not in BACKGROUND_EVENT_KINDS)
|
||||
for domain, kinds in EVENT_KINDS.items()
|
||||
@@ -85,6 +88,21 @@ def is_scoreable_event(event: LifeEvent) -> bool:
|
||||
return event["event_kind"] not in BACKGROUND_EVENT_KINDS
|
||||
|
||||
|
||||
def is_primary_scoreable_event(event: LifeEvent) -> bool:
|
||||
"""Dated events that may move the candidate ranking as a primary formula."""
|
||||
return is_scoreable_event(event) and event["event_kind"] not in AUXILIARY_EVENT_KINDS
|
||||
|
||||
|
||||
def subject_for_event_domain(domain: str, subject: str | None = None) -> Literal["self", "family", "other"]:
|
||||
if domain == "family":
|
||||
return "family"
|
||||
if domain == "other":
|
||||
return "other"
|
||||
if subject in {"self", "family", "other"}:
|
||||
return cast(Literal["self", "family", "other"], subject)
|
||||
return "self"
|
||||
|
||||
|
||||
def _bounded_number(body: dict[str, Any], name: str, minimum: float, maximum: float) -> float:
|
||||
value = body.get(name)
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(float(value)):
|
||||
@@ -167,12 +185,11 @@ def normalize_rectification_request(body: Any, *, today: date | None = None) ->
|
||||
summary = raw_event.get("summary", "")
|
||||
if not isinstance(summary, str) or len(summary) > 1_000:
|
||||
raise ValueError(f"events[{index}].summary must be a string up to 1000 characters")
|
||||
subject = raw_event.get("subject")
|
||||
if subject is None:
|
||||
subject = "family" if domain == "family" else "other" if domain == "other" else "self"
|
||||
if subject not in {"self", "family", "other"}:
|
||||
raw_subject = raw_event.get("subject")
|
||||
if raw_subject is not None and raw_subject not in {"self", "family", "other"}:
|
||||
raise ValueError(f"events[{index}].subject is invalid")
|
||||
if event_kind not in BACKGROUND_EVENT_KINDS and subject != "self":
|
||||
subject = subject_for_event_domain(cast(str, domain), None if raw_subject is None else str(raw_subject))
|
||||
if event_kind not in BACKGROUND_EVENT_KINDS and domain != "family" and subject != "self":
|
||||
raise ValueError(f"events[{index}].subject must be self for scoreable events")
|
||||
cleaned_event: dict[str, Any] = {
|
||||
"id": event_id,
|
||||
|
||||
Reference in New Issue
Block a user