fix(rectification): anchor candidate windows to civil dates across midnight
Carry explicit local date intervals instead of inferring the day from clock order. Cluster width, delivery, adoption, and reports keep the actual civil date; adopted date is stored separately from the reported birth_date. Algorithm identity is scoring-9 / spec-v5. Scoring weights, confirmation thresholds, and Skill version are unchanged. Isolated Linux final-3 gates passed; four pre-existing Python failures remain. This is not a production release.
This commit is contained in:
@@ -88,19 +88,12 @@ def _event_datetime(event: LifeEvent) -> datetime:
|
||||
|
||||
|
||||
def _candidate_datetimes(request: RectificationEventRequest) -> list[datetime]:
|
||||
birth_date = date.fromisoformat(request["birth_date"])
|
||||
start = datetime.combine(birth_date, time.fromisoformat(request["start_time"]))
|
||||
end = datetime.combine(birth_date, time.fromisoformat(request["end_time"]))
|
||||
if end < start:
|
||||
end += timedelta(days=1)
|
||||
minute_count = int((end - start).total_seconds() // 60) + 1
|
||||
if minute_count < 1 or minute_count > 1_440:
|
||||
raise RectificationEventCalculationError("candidate_range_out_of_bounds")
|
||||
raw_step = request.get("minute_step") if isinstance(request, dict) else None
|
||||
step = int(raw_step or 1)
|
||||
if step < 1 or step > 15:
|
||||
raise RectificationEventCalculationError("minute_step_out_of_bounds")
|
||||
return [start + timedelta(minutes=offset) for offset in range(0, minute_count, step)]
|
||||
from scripts.rectification.candidate_window import enumerate_candidate_window
|
||||
|
||||
try:
|
||||
return enumerate_candidate_window(request)
|
||||
except ValueError as exc:
|
||||
raise RectificationEventCalculationError(str(exc)) from exc
|
||||
|
||||
|
||||
def _active_vimshottari(
|
||||
@@ -611,7 +604,11 @@ def compute_candidate_static_contexts(
|
||||
candidates: Sequence[datetime] | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
candidate_datetimes = list(candidates) if candidates is not None else _candidate_datetimes(request)
|
||||
return [build_candidate_static_context(request, candidate) for candidate in candidate_datetimes]
|
||||
from scripts.rectification.candidate_window import candidate_positions
|
||||
|
||||
positions = candidate_positions(request, candidate_datetimes)
|
||||
return [{**build_candidate_static_context(request, candidate), **position}
|
||||
for candidate, position in zip(candidate_datetimes, positions)]
|
||||
|
||||
|
||||
def _candidate_row(
|
||||
@@ -737,6 +734,9 @@ def _canonical_input_contract(request: RectificationEventRequest) -> tuple[dict,
|
||||
"ephemeris_source": "swisseph_calc_ut",
|
||||
},
|
||||
}
|
||||
if "candidate_intervals" in request:
|
||||
payload["schema_version"] = "rectification-candidate-input-v3"
|
||||
payload["candidate_intervals"] = request["candidate_intervals"]
|
||||
normalized = json.dumps(payload, ensure_ascii=True, sort_keys=True, separators=(",", ":"))
|
||||
return payload, hashlib.sha256(normalized.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user