Cache work that depends only on the candidate minute, drop unread response fields, and reject windows above 241 candidates before any chart is built. Eleven canonical scenario hashes match 77057a819. Sealed and reported-offset replays match the 2026-10-08 records (changed_trial_count 8 and 581) and are stored as scoring_perf_2026_10_13.
Co-Authored-By: Grok (agent loop)
246 lines
9.8 KiB
Python
246 lines
9.8 KiB
Python
"""Baseline copies for TASK-rect-scoring-perf-20261013.
|
|
|
|
Source commit: 77057a819
|
|
git show 77057a819:scripts/rectification/event_probes.py sha256: 9bbadc246e6b8e94bb047522c3e5645613f508b3473ef40b38a207c77fdad58c
|
|
git show 77057a819:scripts/rectification/dasha_transition_proximity.py sha256: 0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8
|
|
|
|
The helpers these functions call are unchanged by this task and are imported
|
|
from the current modules. Only guided_collect_windows and
|
|
merge_transition_proximity differ from the copies below.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import date, datetime
|
|
from typing import Any, Sequence
|
|
|
|
from scripts.rectification.dasha_transition_proximity import (
|
|
_context_time as _proximity_context_time,
|
|
representative_event_date,
|
|
score_transition_proximity,
|
|
)
|
|
from scripts.rectification.event_probes import (
|
|
GUIDED_ANY_DOMAIN,
|
|
GUIDED_COLLECT_LIMIT,
|
|
GUIDED_DOMAIN_ORDER,
|
|
_GUIDED_TRACK_DOMAIN,
|
|
_birth_year,
|
|
_context_time as _probe_context_time,
|
|
_guided_track_starts,
|
|
_guided_year_windows,
|
|
_narayana_start_dates,
|
|
_remaining_contexts,
|
|
_representative_pairs,
|
|
_scoreable,
|
|
_static_contexts,
|
|
_vim_start_dates,
|
|
)
|
|
|
|
# Both copies call `_context_time`. They are rebound onto separate globals
|
|
# below so each keeps the helper it had in its source module.
|
|
_context_time = _probe_context_time
|
|
|
|
def guided_collect_windows(
|
|
request: dict[str, Any],
|
|
built: dict[str, Any],
|
|
*,
|
|
candidate_times: Sequence[str],
|
|
today: date | None = None,
|
|
) -> list[dict[str, Any]]:
|
|
"""Boundary windows for guided collect. Does not change auto probe generation."""
|
|
birth_date = str(request.get("birth_date") or "").strip()
|
|
birth_year = _birth_year(birth_date)
|
|
if birth_year is None:
|
|
return []
|
|
try:
|
|
datetime.strptime(birth_date, "%Y-%m-%d")
|
|
except ValueError:
|
|
return []
|
|
remaining = _remaining_contexts(built, candidate_times)
|
|
if len(remaining) < 2:
|
|
remaining = _static_contexts(built)
|
|
work = [item for item in remaining if _scoreable(item)]
|
|
if len(work) < 2:
|
|
return []
|
|
declined = {
|
|
str(item).strip()
|
|
for item in (request.get("declined_domains") or [])
|
|
if str(item).strip() in GUIDED_DOMAIN_ORDER
|
|
}
|
|
now = today or date.today()
|
|
lo, hi = birth_year + 5, min(now.year, birth_year + 80)
|
|
vim_cache: dict[tuple[Any, ...], list[date]] = {}
|
|
narayana_cache: dict[tuple[Any, ...], list[date] | None] = {}
|
|
starts_by_time: dict[str, dict[str, list[date]]] = {}
|
|
for context in work:
|
|
time = _context_time(context)
|
|
if not time:
|
|
continue
|
|
starts_by_time[time] = _guided_track_starts(
|
|
context,
|
|
birth_date=birth_date,
|
|
lo=lo,
|
|
hi=hi,
|
|
include_pratyantar=True,
|
|
varga_narayana=True,
|
|
vim_cache=vim_cache,
|
|
narayana_cache=narayana_cache,
|
|
)
|
|
rows: list[dict[str, Any]] = []
|
|
seen: set[tuple[int, int, int, str]] = set()
|
|
for left, right in _representative_pairs(work):
|
|
left_time = str(_context_time(left) or "")
|
|
right_time = str(_context_time(right) or "")
|
|
left_tracks = starts_by_time.get(left_time) or {}
|
|
right_tracks = starts_by_time.get(right_time) or {}
|
|
for track in ("vim", "nara", "nara:d9", "nara:d10"):
|
|
left_dates = left_tracks.get(track) or []
|
|
right_dates = right_tracks.get(track) or []
|
|
if not left_dates or not right_dates:
|
|
continue
|
|
for index, (one, two) in enumerate(zip(left_dates, right_dates)):
|
|
if one == two:
|
|
continue
|
|
gathered: list[date] = []
|
|
for context in work:
|
|
time = _context_time(context)
|
|
dates = (starts_by_time.get(time or "") or {}).get(track) or []
|
|
if index < len(dates):
|
|
gathered.append(dates[index])
|
|
if len(gathered) < 2 or min(gathered) == max(gathered):
|
|
continue
|
|
ordered = sorted(gathered)
|
|
cut = ordered[0] + (ordered[-1] - ordered[0]) / 2
|
|
left_n = sum(1 for item in gathered if item <= cut)
|
|
right_n = len(gathered) - left_n
|
|
if left_n < 1 or right_n < 1:
|
|
continue
|
|
# BUG-749: only the D9 / D10 Narayana tracks carry a domain.
|
|
# Vimshottari and natal Narayana boundaries say nothing about
|
|
# which area of life moved, so the window is asked open
|
|
# ("any") instead of being round-robined onto one domain.
|
|
track_domain = _GUIDED_TRACK_DOMAIN.get(track)
|
|
domain = (
|
|
track_domain
|
|
if track_domain and track_domain not in declined
|
|
else GUIDED_ANY_DOMAIN
|
|
)
|
|
for year, month_lo, month_hi in _guided_year_windows(gathered):
|
|
if year < lo or year > hi:
|
|
continue
|
|
key = (year, month_lo, month_hi, domain)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
rows.append({
|
|
"year": year,
|
|
"month_lo": month_lo,
|
|
"month_hi": month_hi,
|
|
"domain": domain,
|
|
"split": {"left": left_n, "right": right_n},
|
|
})
|
|
rows.sort(key=lambda row: (
|
|
abs(row["split"]["left"] - row["split"]["right"]),
|
|
abs(now.year - int(row["year"])),
|
|
-int(row["year"]),
|
|
int(row["month_lo"]),
|
|
str(row["domain"]),
|
|
))
|
|
return rows[:GUIDED_COLLECT_LIMIT]
|
|
|
|
|
|
def merge_transition_proximity(
|
|
matrix: dict[str, dict[str, dict[str, Any]]],
|
|
events: Sequence[dict[str, Any]],
|
|
static_contexts: Sequence[dict[str, Any]],
|
|
birth_date: str,
|
|
*,
|
|
public_technique_layers: Callable[[str, Sequence[str]], list[str]],
|
|
) -> None:
|
|
by_time = {
|
|
time: context
|
|
for context in static_contexts
|
|
if isinstance(context, dict) and (time := _context_time(context))
|
|
}
|
|
vim_cache: dict[tuple[Any, ...], list[date]] = {}
|
|
pd_cache: dict[tuple[Any, ...], list[date]] = {}
|
|
narayana_cache: dict[tuple[Any, ...], list[date] | None] = {}
|
|
for event in events:
|
|
if not isinstance(event, dict):
|
|
continue
|
|
event_id = str(event.get("id") or "")
|
|
cells = matrix.get(event_id)
|
|
if not event_id or not isinstance(cells, dict):
|
|
continue
|
|
event_date = representative_event_date(event)
|
|
if event_date is None:
|
|
continue
|
|
precision = str(event.get("precision") or "")
|
|
lo, hi = event_date.year - 1, event_date.year + 1
|
|
for time, cell in cells.items():
|
|
context = by_time.get(str(time)[:5])
|
|
if not isinstance(cell, dict) or not isinstance(context, dict):
|
|
continue
|
|
moon = (context.get("planet_longitudes") or {}).get("Moon")
|
|
if not isinstance(moon, (int, float)):
|
|
continue
|
|
# Native contexts retain the local date even when the window crosses midnight.
|
|
# Legacy time-only contexts still use the request's date.
|
|
candidate_at = context.get("candidate_at")
|
|
candidate_date = candidate_at.date().isoformat() if isinstance(candidate_at, datetime) else birth_date
|
|
vim_key = (candidate_date, round(float(moon), 6), lo, hi)
|
|
if vim_key not in vim_cache:
|
|
vim_cache[vim_key] = _vim_start_dates(candidate_date, float(moon), lo, hi)
|
|
pd_cache[vim_key] = _vim_start_dates(
|
|
candidate_date,
|
|
float(moon),
|
|
lo,
|
|
hi,
|
|
include_pratyantar=True,
|
|
)
|
|
planets = context.get("planet_longitudes") or {}
|
|
asc = context.get("ascendant_index")
|
|
narayana_key = (
|
|
candidate_date,
|
|
int(asc) if isinstance(asc, int) else None,
|
|
lo,
|
|
hi,
|
|
round(float(moon), 6),
|
|
)
|
|
if narayana_key not in narayana_cache:
|
|
narayana_cache[narayana_key] = (
|
|
_narayana_start_dates(int(asc), planets, candidate_date, lo, hi)
|
|
if isinstance(asc, int) and isinstance(planets, dict)
|
|
else None
|
|
)
|
|
ad_starts = vim_cache[vim_key]
|
|
ad_set = set(ad_starts)
|
|
pd_only = [item for item in pd_cache[vim_key] if item not in ad_set]
|
|
scored = score_transition_proximity(
|
|
event_date=event_date,
|
|
precision=precision,
|
|
vim_starts=ad_starts,
|
|
vim_pd_starts=pd_only,
|
|
narayana_starts=narayana_cache[narayana_key] or [],
|
|
)
|
|
if scored["points"] <= 0 and not scored["rule_ids"]:
|
|
continue
|
|
cell["points"] = round(float(cell.get("points") or 0) + float(scored["points"]), 4)
|
|
cell["rule_ids"] = sorted({
|
|
*list(cell.get("rule_ids") or []),
|
|
*scored["rule_ids"],
|
|
})
|
|
domain = str(event.get("domain") or cell.get("domain") or "")
|
|
cell["technique_layers"] = public_technique_layers(domain, cell["rule_ids"])
|
|
|
|
|
|
import types as _types
|
|
|
|
def _rebind(function, context_time):
|
|
globs = dict(function.__globals__)
|
|
globs["_context_time"] = context_time
|
|
return _types.FunctionType(function.__code__, globs, function.__name__)
|
|
|
|
|
|
guided_collect_windows = _rebind(guided_collect_windows, _probe_context_time)
|
|
merge_transition_proximity = _rebind(merge_transition_proximity, _proximity_context_time)
|