fix(rectification): keep hour-window tail clusters and lock the search window (BUG-623, BUG-624, BUG-625)
Hour windows no longer drop later signature clusters. Credible range uses cluster coverage, and a mid-session spoken birth window gets a fixed reply without calling the model. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,7 +21,7 @@ PROBE_PHASE_HOLDOUT_VALIDATION = "holdout_validation"
|
||||
|
||||
MIN_DISCRIMINATOR_EVENTS = 3
|
||||
MIN_DISCRIMINATOR_DOMAINS = 2
|
||||
MAX_PUBLIC_CLUSTERS = 12
|
||||
MAX_PUBLIC_CLUSTERS = 64
|
||||
SIGNATURE_LAYERS = ("d1", "d9", "d10", "d24", "d4", "d12", "md")
|
||||
NAKSHATRA_SPAN = 360.0 / 27.0
|
||||
LAYER_VARGA = {
|
||||
@@ -235,6 +235,55 @@ def expand_times_through_clusters(
|
||||
return expanded
|
||||
|
||||
|
||||
def _cluster_peak_score(cluster: dict[str, Any], by_time: dict[str, dict[str, Any]]) -> float:
|
||||
scores = [
|
||||
float(by_time[time].get("score") or 0)
|
||||
for time in cluster.get("times") or []
|
||||
if time in by_time
|
||||
]
|
||||
return max(scores) if scores else 0.0
|
||||
|
||||
|
||||
def cap_clusters_by_adjacent_merge(
|
||||
clusters: Sequence[dict[str, Any]],
|
||||
by_time: dict[str, dict[str, Any]],
|
||||
max_clusters: int = MAX_PUBLIC_CLUSTERS,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Keep the whole window. If there are too many clusters, merge adjacent weak ones."""
|
||||
work = [
|
||||
{
|
||||
**cluster,
|
||||
"times": list(cluster.get("times") or []),
|
||||
"contexts": list(cluster.get("contexts") or []),
|
||||
}
|
||||
for cluster in clusters
|
||||
if cluster.get("times")
|
||||
]
|
||||
while len(work) > max_clusters:
|
||||
best_index = 0
|
||||
best_key: tuple[float, float, int] | None = None
|
||||
for index in range(len(work) - 1):
|
||||
left = _cluster_peak_score(work[index], by_time)
|
||||
right = _cluster_peak_score(work[index + 1], by_time)
|
||||
key = (min(left, right), left + right, index)
|
||||
if best_key is None or key < best_key:
|
||||
best_key = key
|
||||
best_index = index
|
||||
left = work[best_index]
|
||||
right = work[best_index + 1]
|
||||
merged_times = sorted(set(left["times"] + right["times"]), key=_clock)
|
||||
work[best_index] = {
|
||||
"signature": left.get("signature"),
|
||||
"signature_key": f"{left.get('signature_key')}+{right.get('signature_key')}",
|
||||
"contexts": list(left.get("contexts") or []) + list(right.get("contexts") or []),
|
||||
"times": merged_times,
|
||||
"representative_time": merged_times[len(merged_times) // 2],
|
||||
"representative": left.get("representative") or right.get("representative"),
|
||||
}
|
||||
del work[best_index + 1]
|
||||
return work
|
||||
|
||||
|
||||
def select_signature_representatives(
|
||||
rows: Sequence[dict[str, Any]],
|
||||
static_contexts: Sequence[dict[str, Any]] | None = None,
|
||||
@@ -251,14 +300,17 @@ def select_signature_representatives(
|
||||
clusters = cluster_contexts_by_signature(contexts)
|
||||
else:
|
||||
clusters = _adjacent_score_clusters(list(by_time.values()))
|
||||
clusters = cap_clusters_by_adjacent_merge(clusters, by_time)
|
||||
representatives: list[dict[str, Any]] = []
|
||||
for cluster in clusters:
|
||||
members = [by_time[time] for time in cluster["times"] if time in by_time]
|
||||
if not members:
|
||||
continue
|
||||
representatives.append(max(members, key=lambda row: (float(row.get("score") or 0), str(row.get("time")))))
|
||||
if len(representatives) >= MAX_PUBLIC_CLUSTERS:
|
||||
break
|
||||
best = max(members, key=lambda row: (float(row.get("score") or 0), str(row.get("time"))))
|
||||
representatives.append({
|
||||
**best,
|
||||
"cluster_times": [time for time in cluster["times"] if time in by_time],
|
||||
})
|
||||
representatives.sort(key=lambda row: (-float(row.get("score") or 0), str(row.get("time"))))
|
||||
return representatives or list(rows)[:1]
|
||||
|
||||
|
||||
@@ -402,16 +402,40 @@ def build_candidate_decisions(
|
||||
abs(score - other) <= TIE_ABSOLUTE_TOLERANCE
|
||||
for other in all_scores
|
||||
)
|
||||
cluster_times, cluster_start, cluster_end = _cluster_span(row)
|
||||
decisions.append({
|
||||
"candidate_id": str(uuid5(NAMESPACE_URL, f"{POLICY_VERSION}:{result_id}:{row['time']}")),
|
||||
"rank": index + 1,
|
||||
"time": row["time"],
|
||||
"relative_support": supports[index],
|
||||
"tied_minute_count": tied_minute_count,
|
||||
"cluster_times": cluster_times,
|
||||
"cluster_start": cluster_start,
|
||||
"cluster_end": cluster_end,
|
||||
})
|
||||
return decisions
|
||||
|
||||
|
||||
def _cluster_span(row: dict[str, Any]) -> tuple[list[str], str, str]:
|
||||
raw = row.get("cluster_times")
|
||||
times: list[str] = []
|
||||
seen: set[str] = set()
|
||||
values = raw if isinstance(raw, list) and raw else [row.get("time")]
|
||||
for item in values:
|
||||
text = str(item or "")[:5]
|
||||
if len(text) != 5 or text[2] != ":" or text in seen:
|
||||
continue
|
||||
seen.add(text)
|
||||
times.append(text)
|
||||
if not times:
|
||||
clock = str(row.get("time") or "")[:5]
|
||||
times = [clock] if len(clock) == 5 and clock[2] == ":" else []
|
||||
times.sort(key=lambda value: int(value[:2]) * 60 + int(value[3:5]))
|
||||
start = times[0] if times else str(row.get("time") or "")[:5]
|
||||
end = times[-1] if times else start
|
||||
return times, start, end
|
||||
|
||||
|
||||
def _gate(passed: bool, **details: Any) -> dict[str, Any]:
|
||||
return {"passed": passed, **details}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user